Merge pull request #3342 from ey6es/metavoxels

Very basic beginnings of an implementation of the dual contour algorithm, including necessary data representation and tools.
This commit is contained in:
Philip Rosedale 2014-09-04 21:46:14 -07:00
commit 886add11db
26 changed files with 3339 additions and 1016 deletions

View file

@ -0,0 +1,26 @@
#version 120
//
// directional_light.frag
// fragment shader
//
// Created by Andrzej Kapolka on 9/3/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 normal texture
uniform sampler2D normalMap;
void main(void) {
// compute the base color based on OpenGL lighting model
vec4 normal = texture2D(normalMap, gl_TexCoord[0].st);
gl_FragColor = vec4((texture2D(diffuseMap, gl_TexCoord[0].st) * (gl_FrontLightModelProduct.sceneColor +
gl_FrontLightProduct[0].ambient + gl_FrontLightProduct[0].diffuse * max(0.0, dot(normal * 2.0 -
vec4(1.0, 1.0, 1.0, 2.0), gl_LightSource[0].position)))).rgb, normal.a);
}

View file

@ -1,10 +1,10 @@
#version 120
//
// metavoxel_heightfield.frag
// directional_light.frag
// fragment shader
//
// Created by Andrzej Kapolka on 7/28/14.
// Created by Andrzej Kapolka on 9/3/14.
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
@ -14,6 +14,12 @@
// the diffuse texture
uniform sampler2D diffuseMap;
// the normal texture
uniform sampler2D normalMap;
// the depth texture
uniform sampler2D depthMap;
// the shadow texture
uniform sampler2DShadow shadowMap;
@ -21,28 +27,39 @@ uniform sampler2DShadow shadowMap;
uniform vec3 shadowDistances;
// the inverse of the size of the shadow map
const float shadowScale = 1.0 / 2048.0;
uniform float shadowScale;
// the interpolated position
varying vec4 position;
// the distance to the near clip plane
uniform float near;
// the interpolated normal
varying vec4 normal;
// scale factor for depth: (far - near) / far
uniform float depthScale;
// offset for depth texture coordinates
uniform vec2 depthTexCoordOffset;
// scale for depth texture coordinates
uniform vec2 depthTexCoordScale;
void main(void) {
// compute the view space position using the depth
float z = near / (texture2D(depthMap, gl_TexCoord[0].st).r * depthScale - 1.0);
vec4 position = vec4((depthTexCoordOffset + gl_TexCoord[0].st * depthTexCoordScale) * z, z, 1.0);
// compute the index of the cascade to use and the corresponding texture coordinates
int shadowIndex = int(dot(step(vec3(position.z), shadowDistances), vec3(1.0, 1.0, 1.0)));
vec3 shadowTexCoord = vec3(dot(gl_EyePlaneS[shadowIndex], position), dot(gl_EyePlaneT[shadowIndex], position),
dot(gl_EyePlaneR[shadowIndex], position));
// compute the base color based on OpenGL lighting model
float diffuse = dot(normalize(normal), gl_LightSource[0].position);
// compute the color based on OpenGL lighting model, use the alpha from the normal map
vec4 normal = texture2D(normalMap, gl_TexCoord[0].st);
float diffuse = dot(normal * 2.0 - vec4(1.0, 1.0, 1.0, 2.0), gl_LightSource[0].position);
float facingLight = step(0.0, diffuse) * 0.25 *
(shadow2D(shadowMap, shadowTexCoord + vec3(-shadowScale, -shadowScale, 0.0)).r +
shadow2D(shadowMap, shadowTexCoord + vec3(-shadowScale, shadowScale, 0.0)).r +
shadow2D(shadowMap, shadowTexCoord + vec3(shadowScale, -shadowScale, 0.0)).r +
shadow2D(shadowMap, shadowTexCoord + vec3(shadowScale, shadowScale, 0.0)).r);
vec4 base = gl_Color * (gl_FrontLightModelProduct.sceneColor + gl_FrontLightProduct[0].ambient +
gl_FrontLightProduct[0].diffuse * (diffuse * facingLight));
gl_FragColor = base * texture2D(diffuseMap, gl_TexCoord[0].st);
vec4 baseColor = texture2D(diffuseMap, gl_TexCoord[0].st) * (gl_FrontLightModelProduct.sceneColor +
gl_FrontLightProduct[0].ambient + gl_FrontLightProduct[0].diffuse * (diffuse * facingLight));
gl_FragColor = vec4(baseColor.rgb, normal.a);
}

View file

@ -0,0 +1,60 @@
#version 120
//
// directional_light.frag
// fragment shader
//
// Created by Andrzej Kapolka on 9/3/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 normal texture
uniform sampler2D normalMap;
// the depth texture
uniform sampler2D depthMap;
// the shadow texture
uniform sampler2DShadow shadowMap;
// the inverse of the size of the shadow map
uniform float shadowScale;
// the distance to the near clip plane
uniform float near;
// scale factor for depth: (far - near) / far
uniform float depthScale;
// offset for depth texture coordinates
uniform vec2 depthTexCoordOffset;
// scale for depth texture coordinates
uniform vec2 depthTexCoordScale;
void main(void) {
// compute the view space position using the depth
float z = near / (texture2D(depthMap, gl_TexCoord[0].st).r * depthScale - 1.0);
vec4 position = vec4((depthTexCoordOffset + gl_TexCoord[0].st * depthTexCoordScale) * z, z, 1.0);
// compute the corresponding texture coordinates
vec3 shadowTexCoord = vec3(dot(gl_EyePlaneS[0], position), dot(gl_EyePlaneT[0], position), dot(gl_EyePlaneR[0], position));
// compute the color based on OpenGL lighting model, use the alpha from the normal map
vec4 normal = texture2D(normalMap, gl_TexCoord[0].st);
float diffuse = dot(normal * 2.0 - vec4(1.0, 1.0, 1.0, 2.0), gl_LightSource[0].position);
float facingLight = step(0.0, diffuse) * 0.25 *
(shadow2D(shadowMap, shadowTexCoord + vec3(-shadowScale, -shadowScale, 0.0)).r +
shadow2D(shadowMap, shadowTexCoord + vec3(-shadowScale, shadowScale, 0.0)).r +
shadow2D(shadowMap, shadowTexCoord + vec3(shadowScale, -shadowScale, 0.0)).r +
shadow2D(shadowMap, shadowTexCoord + vec3(shadowScale, shadowScale, 0.0)).r);
vec4 baseColor = texture2D(diffuseMap, gl_TexCoord[0].st) * (gl_FrontLightModelProduct.sceneColor +
gl_FrontLightProduct[0].ambient + gl_FrontLightProduct[0].diffuse * (diffuse * facingLight));
gl_FragColor = vec4(baseColor.rgb, normal.a);
}

View file

@ -1,25 +0,0 @@
#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);
}

View file

@ -1,51 +0,0 @@
#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 distance between height points in texture space
uniform float heightScale;
// the scale between height and color textures
uniform float colorScale;
// the interpolated position
varying vec4 position;
// the interpolated normal
varying vec4 normal;
void main(void) {
// transform and store the normal for interpolation
vec2 heightCoord = gl_MultiTexCoord0.st;
float deltaX = texture2D(heightMap, heightCoord - vec2(heightScale, 0.0)).r -
texture2D(heightMap, heightCoord + vec2(heightScale, 0.0)).r;
float deltaZ = texture2D(heightMap, heightCoord - vec2(0.0, heightScale)).r -
texture2D(heightMap, heightCoord + vec2(0.0, heightScale)).r;
normal = normalize(gl_ModelViewMatrix * vec4(deltaX, heightScale, deltaZ, 0.0));
// add the height to the position
float height = texture2D(heightMap, heightCoord).r;
position = gl_ModelViewMatrix * (gl_Vertex + vec4(0.0, height, 0.0, 0.0));
gl_Position = gl_ProjectionMatrix * position;
// the zero height should be invisible
gl_FrontColor = vec4(1.0, 1.0, 1.0, step(height, 0.0));
// pass along the scaled/offset texture coordinates
gl_TexCoord[0] = (gl_MultiTexCoord0 - vec4(heightScale, heightScale, 0.0, 0.0)) * colorScale;
// and the shadow texture coordinates
gl_TexCoord[1] = vec4(dot(gl_EyePlaneS[0], position), dot(gl_EyePlaneT[0], position), dot(gl_EyePlaneR[0], position), 1.0);
}

View file

@ -14,7 +14,11 @@
// the diffuse texture
uniform sampler2D diffuseMap;
// the interpolated normal
varying vec4 normal;
void main(void) {
// compute the base color based on OpenGL lighting model
gl_FragColor = gl_Color * texture2D(diffuseMap, gl_TexCoord[0].st);
gl_FragData[0] = gl_Color * texture2D(diffuseMap, gl_TexCoord[0].st);
gl_FragData[1] = normalize(normal) * 0.5 + vec4(0.5, 0.5, 0.5, 1.0);
}

View file

@ -20,9 +20,20 @@ uniform float heightScale;
// the scale between height and color textures
uniform float colorScale;
// the interpolated normal
varying vec4 normal;
void main(void) {
// transform and store the normal for interpolation
vec2 heightCoord = gl_MultiTexCoord0.st;
float deltaX = texture2D(heightMap, heightCoord - vec2(heightScale, 0.0)).r -
texture2D(heightMap, heightCoord + vec2(heightScale, 0.0)).r;
float deltaZ = texture2D(heightMap, heightCoord - vec2(0.0, heightScale)).r -
texture2D(heightMap, heightCoord + vec2(0.0, heightScale)).r;
normal = normalize(gl_ModelViewMatrix * vec4(deltaX, heightScale, deltaZ, 0.0));
// add the height to the position
float height = texture2D(heightMap, gl_MultiTexCoord0.st).r;
float height = texture2D(heightMap, heightCoord).r;
gl_Position = gl_ModelViewProjectionMatrix * (gl_Vertex + vec4(0.0, height, 0.0, 0.0));
// the zero height should be invisible

View file

@ -1,21 +0,0 @@
#version 120
//
// metavoxel_heightfield_light.frag
// fragment shader
//
// Created by Andrzej Kapolka on 8/20/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 interpolated normal
varying vec4 normal;
void main(void) {
// compute the base color based on OpenGL lighting model
gl_FragColor = gl_Color * (gl_FrontLightModelProduct.sceneColor + gl_FrontLightProduct[0].ambient +
gl_FrontLightProduct[0].diffuse * max(0.0, dot(normalize(normal), gl_LightSource[0].position)));
}

View file

@ -1,45 +0,0 @@
#version 120
//
// metavoxel_heighfield_light.vert
// vertex shader
//
// Created by Andrzej Kapolka on 8/20/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 distance between height points in texture space
uniform float heightScale;
// the interpolated position
varying vec4 position;
// the interpolated normal
varying vec4 normal;
void main(void) {
// transform and store the normal for interpolation
vec2 heightCoord = gl_MultiTexCoord0.st;
float deltaX = texture2D(heightMap, heightCoord - vec2(heightScale, 0.0)).r -
texture2D(heightMap, heightCoord + vec2(heightScale, 0.0)).r;
float deltaZ = texture2D(heightMap, heightCoord - vec2(0.0, heightScale)).r -
texture2D(heightMap, heightCoord + vec2(0.0, heightScale)).r;
normal = normalize(gl_ModelViewMatrix * vec4(deltaX, heightScale, deltaZ, 0.0));
// add the height to the position
float height = texture2D(heightMap, heightCoord).r;
position = gl_ModelViewMatrix * (gl_Vertex + vec4(0.0, height, 0.0, 0.0));
gl_Position = gl_ProjectionMatrix * position;
// the zero height should be invisible
gl_FrontColor = vec4(1.0, 1.0, 1.0, step(height, 0.0));
// and the shadow texture coordinates
gl_TexCoord[1] = vec4(dot(gl_EyePlaneS[0], position), dot(gl_EyePlaneT[0], position), dot(gl_EyePlaneR[0], position), 1.0);
}

View file

@ -1,44 +0,0 @@
#version 120
//
// metavoxel_heightfield_light_cascaded_shadow_map.frag
// fragment shader
//
// Created by Andrzej Kapolka on 8/20/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 shadow texture
uniform sampler2DShadow shadowMap;
// the distances to the cascade sections
uniform vec3 shadowDistances;
// the inverse of the size of the shadow map
const float shadowScale = 1.0 / 2048.0;
// the interpolated position
varying vec4 position;
// the interpolated normal
varying vec4 normal;
void main(void) {
// compute the index of the cascade to use and the corresponding texture coordinates
int shadowIndex = int(dot(step(vec3(position.z), shadowDistances), vec3(1.0, 1.0, 1.0)));
vec3 shadowTexCoord = vec3(dot(gl_EyePlaneS[shadowIndex], position), dot(gl_EyePlaneT[shadowIndex], position),
dot(gl_EyePlaneR[shadowIndex], position));
// compute the base color based on OpenGL lighting model
float diffuse = dot(normalize(normal), gl_LightSource[0].position);
float facingLight = step(0.0, diffuse) * 0.25 *
(shadow2D(shadowMap, shadowTexCoord + vec3(-shadowScale, -shadowScale, 0.0)).r +
shadow2D(shadowMap, shadowTexCoord + vec3(-shadowScale, shadowScale, 0.0)).r +
shadow2D(shadowMap, shadowTexCoord + vec3(shadowScale, -shadowScale, 0.0)).r +
shadow2D(shadowMap, shadowTexCoord + vec3(shadowScale, shadowScale, 0.0)).r);
gl_FragColor = gl_Color * (gl_FrontLightModelProduct.sceneColor + gl_FrontLightProduct[0].ambient +
gl_FrontLightProduct[0].diffuse * (diffuse * facingLight));
}

View file

@ -1,33 +0,0 @@
#version 120
//
// metavoxel_heightfield_light_shadow_map.frag
// fragment shader
//
// Created by Andrzej Kapolka on 8/20/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 shadow texture
uniform sampler2DShadow shadowMap;
// the inverse of the size of the shadow map
const float shadowScale = 1.0 / 2048.0;
// the interpolated normal
varying vec4 normal;
void main(void) {
// compute the base color based on OpenGL lighting model
float diffuse = dot(normalize(normal), gl_LightSource[0].position);
float facingLight = step(0.0, diffuse) * 0.25 *
(shadow2D(shadowMap, gl_TexCoord[1].stp + vec3(-shadowScale, -shadowScale, 0.0)).r +
shadow2D(shadowMap, gl_TexCoord[1].stp + vec3(-shadowScale, shadowScale, 0.0)).r +
shadow2D(shadowMap, gl_TexCoord[1].stp + vec3(shadowScale, -shadowScale, 0.0)).r +
shadow2D(shadowMap, gl_TexCoord[1].stp + vec3(shadowScale, shadowScale, 0.0)).r);
gl_FragColor = gl_Color * (gl_FrontLightModelProduct.sceneColor + gl_FrontLightProduct[0].ambient +
gl_FrontLightProduct[0].diffuse * (diffuse * facingLight));
}

View file

@ -1,37 +0,0 @@
#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 shadow texture
uniform sampler2DShadow shadowMap;
// the inverse of the size of the shadow map
const float shadowScale = 1.0 / 2048.0;
// the interpolated normal
varying vec4 normal;
void main(void) {
// compute the base color based on OpenGL lighting model
float diffuse = dot(normalize(normal), gl_LightSource[0].position);
float facingLight = step(0.0, diffuse) * 0.25 *
(shadow2D(shadowMap, gl_TexCoord[1].stp + vec3(-shadowScale, -shadowScale, 0.0)).r +
shadow2D(shadowMap, gl_TexCoord[1].stp + vec3(-shadowScale, shadowScale, 0.0)).r +
shadow2D(shadowMap, gl_TexCoord[1].stp + vec3(shadowScale, -shadowScale, 0.0)).r +
shadow2D(shadowMap, gl_TexCoord[1].stp + vec3(shadowScale, shadowScale, 0.0)).r);
vec4 base = gl_Color * (gl_FrontLightModelProduct.sceneColor + gl_FrontLightProduct[0].ambient +
gl_FrontLightProduct[0].diffuse * (diffuse * facingLight));
gl_FragColor = base * texture2D(diffuseMap, gl_TexCoord[0].st);
}

View file

@ -0,0 +1,21 @@
#version 120
//
// metavoxel_voxel_base.frag
// fragment shader
//
// Created by Andrzej Kapolka on 9/4/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 interpolated normal
varying vec4 normal;
void main(void) {
// store the interpolated color and normal
gl_FragData[0] = gl_Color;
gl_FragData[1] = normalize(normal) * 0.5 + vec4(0.5, 0.5, 0.5, 1.0);
}

View file

@ -0,0 +1,26 @@
#version 120
//
// metavoxel_voxel_base.vert
// vertex shader
//
// Created by Andrzej Kapolka on 9/4/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 interpolated normal
varying vec4 normal;
void main(void) {
// transform and store the normal for interpolation
normal = vec4(normalize(gl_NormalMatrix * gl_Normal), 0.0);
// use the fixed-function position
gl_Position = ftransform();
// copy the color for interpolation
gl_FrontColor = vec4(gl_Color.rgb, 0.0);
}

View file

@ -0,0 +1,29 @@
#version 120
//
// metavoxel_voxel_splat.frag
// fragment shader
//
// Created by Andrzej Kapolka on 9/4/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 number of splats per pass
const int SPLAT_COUNT = 4;
// the splat textures
uniform sampler2D diffuseMaps[SPLAT_COUNT];
// alpha values for the four splat textures
varying vec4 alphaValues;
void main(void) {
// blend the splat textures
gl_FragColor = (texture2D(diffuseMaps[0], gl_TexCoord[0].st) * alphaValues.x +
texture2D(diffuseMaps[1], gl_TexCoord[1].st) * alphaValues.y +
texture2D(diffuseMaps[2], gl_TexCoord[2].st) * alphaValues.z +
texture2D(diffuseMaps[3], gl_TexCoord[3].st) * alphaValues.w);
}

View file

@ -0,0 +1,62 @@
#version 120
//
// metavoxel_voxel_splat.vert
// vertex shader
//
// Created by Andrzej Kapolka on 9/4/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 splat textures scales on the S axis
uniform vec4 splatTextureScalesS;
// the splat texture scales on the T axis
uniform vec4 splatTextureScalesT;
// the lower bounds of the values corresponding to the splat textures
uniform vec4 textureValueMinima;
// the upper bounds of the values corresponding to the splat textures
uniform vec4 textureValueMaxima;
// the materials to apply to the vertex
attribute vec4 materials;
// the weights of each material
attribute vec4 materialWeights;
// alpha values for the four splat textures
varying vec4 alphaValues;
void main(void) {
// use the fixed-function position
gl_Position = ftransform();
// pass along the scaled/offset texture coordinates
vec4 textureSpacePosition = vec4(gl_Vertex.xz, 0.0, 1.0);
gl_TexCoord[0] = textureSpacePosition * vec4(splatTextureScalesS[0], splatTextureScalesT[0], 0.0, 1.0);
gl_TexCoord[1] = textureSpacePosition * vec4(splatTextureScalesS[1], splatTextureScalesT[1], 0.0, 1.0);
gl_TexCoord[2] = textureSpacePosition * vec4(splatTextureScalesS[2], splatTextureScalesT[2], 0.0, 1.0);
gl_TexCoord[3] = textureSpacePosition * vec4(splatTextureScalesS[3], splatTextureScalesT[3], 0.0, 1.0);
// compute the alpha values for each texture
float value = materials[0];
vec4 valueVector = vec4(value, value, value, value);
alphaValues = step(textureValueMinima, valueVector) * step(valueVector, textureValueMaxima) * materialWeights[0];
value = materials[1];
valueVector = vec4(value, value, value, value);
alphaValues += step(textureValueMinima, valueVector) * step(valueVector, textureValueMaxima) * materialWeights[1];
value = materials[2];
valueVector = vec4(value, value, value, value);
alphaValues += step(textureValueMinima, valueVector) * step(valueVector, textureValueMaxima) * materialWeights[2];
value = materials[3];
valueVector = vec4(value, value, value, value);
alphaValues += step(textureValueMinima, valueVector) * step(valueVector, textureValueMaxima) * materialWeights[3];
}

File diff suppressed because it is too large Load diff

View file

@ -39,6 +39,7 @@ public:
const AttributePointer& getPointBufferAttribute() { return _pointBufferAttribute; }
const AttributePointer& getHeightfieldBufferAttribute() { return _heightfieldBufferAttribute; }
const AttributePointer& getVoxelBufferAttribute() { return _voxelBufferAttribute; }
void simulate(float deltaTime);
void render();
@ -51,6 +52,12 @@ public:
Q_INVOKABLE void deleteTextures(int heightID, int colorID, int textureID);
void noteNeedToLight() { _needToLight = true; }
signals:
void rendering();
protected:
virtual MetavoxelClient* createClient(const SharedNodePointer& node);
@ -59,12 +66,33 @@ private:
void guideToAugmented(MetavoxelVisitor& visitor, bool render = false);
class LightLocations {
public:
int shadowDistances;
int shadowScale;
int nearLocation;
int depthScale;
int depthTexCoordOffset;
int depthTexCoordScale;
};
static void loadLightProgram(const char* name, ProgramObject& program, LightLocations& locations);
AttributePointer _pointBufferAttribute;
AttributePointer _heightfieldBufferAttribute;
AttributePointer _voxelBufferAttribute;
MetavoxelLOD _lod;
QReadWriteLock _lodLock;
Frustum _frustum;
bool _needToLight;
ProgramObject _directionalLight;
LightLocations _directionalLightLocations;
ProgramObject _directionalLightShadowMap;
LightLocations _directionalLightShadowMapLocations;
ProgramObject _directionalLightCascadedShadowMap;
LightLocations _directionalLightCascadedShadowMapLocations;
};
/// Describes contents of a point in a point buffer.
@ -143,8 +171,8 @@ public:
static const int HEIGHT_EXTENSION;
HeightfieldBuffer(const glm::vec3& translation, float scale, const QByteArray& height,
const QByteArray& color, const QByteArray& texture = QByteArray(),
const QVector<SharedObjectPointer>& textures = QVector<SharedObjectPointer>());
const QByteArray& color, const QByteArray& material = QByteArray(),
const QVector<SharedObjectPointer>& materials = QVector<SharedObjectPointer>());
~HeightfieldBuffer();
const glm::vec3& getTranslation() const { return _translation; }
@ -159,10 +187,10 @@ public:
QByteArray& getColor() { return _color; }
const QByteArray& getColor() const { return _color; }
QByteArray& getTexture() { return _texture; }
const QByteArray& getTexture() const { return _texture; }
QByteArray& getMaterial() { return _material; }
const QByteArray& getMaterial() const { return _material; }
const QVector<SharedObjectPointer>& getTextures() const { return _textures; }
const QVector<SharedObjectPointer>& getMaterials() const { return _materials; }
QByteArray getUnextendedHeight() const;
QByteArray getUnextendedColor() const;
@ -183,11 +211,11 @@ private:
Box _colorBounds;
QByteArray _height;
QByteArray _color;
QByteArray _texture;
QVector<SharedObjectPointer> _textures;
QByteArray _material;
QVector<SharedObjectPointer> _materials;
GLuint _heightTextureID;
GLuint _colorTextureID;
GLuint _textureTextureID;
GLuint _materialTextureID;
QVector<NetworkTexturePointer> _networkTextures;
int _heightSize;
float _heightIncrement;
@ -212,6 +240,37 @@ private:
QVector<BufferDataPointer> _buffers;
};
/// Describes contents of a vertex in a voxel buffer.
class VoxelPoint {
public:
glm::vec3 vertex;
quint8 color[3];
char normal[3];
quint8 materials[4];
quint8 materialWeights[4];
};
/// Contains the information necessary to render a voxel block.
class VoxelBuffer : public BufferData {
public:
VoxelBuffer(const QVector<VoxelPoint>& vertices, const QVector<int>& indices,
const QVector<SharedObjectPointer>& materials = QVector<SharedObjectPointer>());
virtual void render(bool cursor = false);
private:
QVector<VoxelPoint> _vertices;
QVector<int> _indices;
int _vertexCount;
int _indexCount;
QOpenGLBuffer _vertexBuffer;
QOpenGLBuffer _indexBuffer;
QVector<SharedObjectPointer> _materials;
QVector<NetworkTexturePointer> _networkTextures;
};
/// A client-side attribute that stores renderable buffers.
class BufferDataAttribute : public InlineAttribute<BufferDataPointer> {
Q_OBJECT
@ -233,42 +292,33 @@ public:
static void init();
static ProgramObject& getHeightfieldProgram() { return _heightfieldProgram; }
static int getHeightScaleLocation() { return _heightScaleLocation; }
static int getColorScaleLocation() { return _colorScaleLocation; }
static ProgramObject& getShadowMapHeightfieldProgram() { return _shadowMapHeightfieldProgram; }
static int getShadowMapHeightScaleLocation() { return _shadowMapHeightScaleLocation; }
static int getShadowMapColorScaleLocation() { return _shadowMapColorScaleLocation; }
static ProgramObject& getCascadedShadowMapHeightfieldProgram() { return _cascadedShadowMapHeightfieldProgram; }
static int getCascadedShadowMapHeightScaleLocation() { return _cascadedShadowMapHeightScaleLocation; }
static int getCascadedShadowMapColorScaleLocation() { return _cascadedShadowMapColorScaleLocation; }
static ProgramObject& getBaseHeightfieldProgram() { return _baseHeightfieldProgram; }
static int getBaseHeightScaleLocation() { return _baseHeightScaleLocation; }
static int getBaseColorScaleLocation() { return _baseColorScaleLocation; }
class SplatLocations {
public:
int heightScale;
int textureScale;
int splatTextureOffset;
int splatTextureScalesS;
int splatTextureScalesT;
int textureValueMinima;
int textureValueMaxima;
int materials;
int materialWeights;
};
static ProgramObject& getSplatHeightfieldProgram() { return _splatHeightfieldProgram; }
static int getSplatHeightScaleLocation() { return _splatHeightScaleLocation; }
static int getSplatTextureScaleLocation() { return _splatTextureScaleLocation; }
static int getSplatTextureOffsetLocation() { return _splatTextureOffsetLocation; }
static int getSplatTextureScalesSLocation() { return _splatTextureScalesSLocation; }
static int getSplatTextureScalesTLocation() { return _splatTextureScalesTLocation; }
static int getSplatTextureValueMinimaLocation() { return _splatTextureValueMinimaLocation; }
static int getSplatTextureValueMaximaLocation() { return _splatTextureValueMaximaLocation; }
static ProgramObject& getLightHeightfieldProgram() { return _lightHeightfieldProgram; }
static int getLightHeightScaleLocation() { return _lightHeightScaleLocation; }
static ProgramObject& getShadowLightHeightfieldProgram() { return _shadowLightHeightfieldProgram; }
static int getShadowLightHeightScaleLocation() { return _shadowLightHeightScaleLocation; }
static ProgramObject& getCascadedShadowLightHeightfieldProgram() { return _cascadedShadowLightHeightfieldProgram; }
static int getCascadedShadowLightHeightScaleLocation() { return _cascadedShadowLightHeightScaleLocation; }
static const SplatLocations& getSplatHeightfieldLocations() { return _splatHeightfieldLocations; }
static ProgramObject& getHeightfieldCursorProgram() { return _heightfieldCursorProgram; }
static ProgramObject& getBaseVoxelProgram() { return _baseVoxelProgram; }
static ProgramObject& getSplatVoxelProgram() { return _splatVoxelProgram; }
static const SplatLocations& getSplatVoxelLocations() { return _splatVoxelLocations; }
Q_INVOKABLE DefaultMetavoxelRendererImplementation();
virtual void augment(MetavoxelData& data, const MetavoxelData& previous, MetavoxelInfo& info, const MetavoxelLOD& lod);
@ -277,27 +327,18 @@ public:
private:
static void loadSplatProgram(const char* type, ProgramObject& program, SplatLocations& locations);
static ProgramObject _pointProgram;
static int _pointScaleLocation;
static ProgramObject _heightfieldProgram;
static int _heightScaleLocation;
static int _colorScaleLocation;
static ProgramObject _shadowMapHeightfieldProgram;
static int _shadowMapHeightScaleLocation;
static int _shadowMapColorScaleLocation;
static ProgramObject _cascadedShadowMapHeightfieldProgram;
static int _cascadedShadowMapHeightScaleLocation;
static int _cascadedShadowMapColorScaleLocation;
static int _shadowDistancesLocation;
static ProgramObject _baseHeightfieldProgram;
static int _baseHeightScaleLocation;
static int _baseColorScaleLocation;
static ProgramObject _splatHeightfieldProgram;
static SplatLocations _splatHeightfieldLocations;
static int _splatHeightScaleLocation;
static int _splatTextureScaleLocation;
static int _splatTextureOffsetLocation;
@ -306,17 +347,11 @@ private:
static int _splatTextureValueMinimaLocation;
static int _splatTextureValueMaximaLocation;
static ProgramObject _lightHeightfieldProgram;
static int _lightHeightScaleLocation;
static ProgramObject _shadowLightHeightfieldProgram;
static int _shadowLightHeightScaleLocation;
static ProgramObject _cascadedShadowLightHeightfieldProgram;
static int _cascadedShadowLightHeightScaleLocation;
static int _shadowLightDistancesLocation;
static ProgramObject _heightfieldCursorProgram;
static ProgramObject _baseVoxelProgram;
static ProgramObject _splatVoxelProgram;
static SplatLocations _splatVoxelLocations;
};
/// Base class for spanner renderers; provides clipping.

View file

@ -29,6 +29,7 @@ TextureCache::TextureCache() :
_whiteTextureID(0),
_blueTextureID(0),
_primaryDepthTextureID(0),
_primaryNormalTextureID(0),
_primaryFramebufferObject(NULL),
_secondaryFramebufferObject(NULL),
_tertiaryFramebufferObject(NULL),
@ -46,6 +47,7 @@ TextureCache::~TextureCache() {
}
if (_primaryFramebufferObject) {
glDeleteTextures(1, &_primaryDepthTextureID);
glDeleteTextures(1, &_primaryNormalTextureID);
}
if (_primaryFramebufferObject) {
@ -71,6 +73,8 @@ void TextureCache::setFrameBufferSize(QSize frameBufferSize) {
_primaryFramebufferObject = NULL;
glDeleteTextures(1, &_primaryDepthTextureID);
_primaryDepthTextureID = 0;
glDeleteTextures(1, &_primaryNormalTextureID);
_primaryNormalTextureID = 0;
}
if (_secondaryFramebufferObject) {
@ -205,15 +209,22 @@ QOpenGLFramebufferObject* TextureCache::getPrimaryFramebufferObject() {
glGenTextures(1, &_primaryDepthTextureID);
glBindTexture(GL_TEXTURE_2D, _primaryDepthTextureID);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, _frameBufferSize.width(), _frameBufferSize.height(),
0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glGenTextures(1, &_primaryNormalTextureID);
glBindTexture(GL_TEXTURE_2D, _primaryNormalTextureID);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, _frameBufferSize.width(), _frameBufferSize.height(),
0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glBindTexture(GL_TEXTURE_2D, 0);
_primaryFramebufferObject->bind();
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, _primaryDepthTextureID, 0);
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, _primaryNormalTextureID, 0);
_primaryFramebufferObject->release();
}
return _primaryFramebufferObject;
@ -225,6 +236,12 @@ GLuint TextureCache::getPrimaryDepthTextureID() {
return _primaryDepthTextureID;
}
GLuint TextureCache::getPrimaryNormalTextureID() {
// ensure that the primary framebuffer object is initialized before returning the normal texture id
getPrimaryFramebufferObject();
return _primaryNormalTextureID;
}
QOpenGLFramebufferObject* TextureCache::getSecondaryFramebufferObject() {
if (!_secondaryFramebufferObject) {
_secondaryFramebufferObject = createFramebufferObject();
@ -278,6 +295,7 @@ bool TextureCache::eventFilter(QObject* watched, QEvent* event) {
delete _primaryFramebufferObject;
_primaryFramebufferObject = NULL;
glDeleteTextures(1, &_primaryDepthTextureID);
glDeleteTextures(1, &_primaryNormalTextureID);
}
if (_secondaryFramebufferObject && _secondaryFramebufferObject->size() != size) {
delete _secondaryFramebufferObject;

View file

@ -61,6 +61,9 @@ public:
/// Returns the ID of the primary framebuffer object's depth texture. This contains the Z buffer used in rendering.
GLuint getPrimaryDepthTextureID();
/// Returns the ID of the primary framebuffer object's normal texture.
GLuint getPrimaryNormalTextureID();
/// Returns a pointer to the secondary framebuffer object, used as an additional render target when performing full
/// screen effects.
QOpenGLFramebufferObject* getSecondaryFramebufferObject();
@ -95,6 +98,7 @@ private:
QHash<QUrl, QWeakPointer<NetworkTexture> > _dilatableNetworkTextures;
GLuint _primaryDepthTextureID;
GLuint _primaryNormalTextureID;
QOpenGLFramebufferObject* _primaryFramebufferObject;
QOpenGLFramebufferObject* _secondaryFramebufferObject;
QOpenGLFramebufferObject* _tertiaryFramebufferObject;

View file

@ -66,13 +66,16 @@ MetavoxelEditor::MetavoxelEditor() :
attributeLayout->addLayout(attributeButtonLayout);
QPushButton* newAttribute = new QPushButton("New...");
attributeButtonLayout->addWidget(newAttribute);
attributeButtonLayout->addWidget(newAttribute, 1);
connect(newAttribute, SIGNAL(clicked()), SLOT(createNewAttribute()));
attributeButtonLayout->addWidget(_deleteAttribute = new QPushButton("Delete"));
attributeButtonLayout->addWidget(_deleteAttribute = new QPushButton("Delete"), 1);
_deleteAttribute->setEnabled(false);
connect(_deleteAttribute, SIGNAL(clicked()), SLOT(deleteSelectedAttribute()));
attributeButtonLayout->addWidget(_showAll = new QCheckBox("Show All"));
connect(_showAll, SIGNAL(clicked()), SLOT(updateAttributes()));
QFormLayout* formLayout = new QFormLayout();
topLayout->addLayout(formLayout);
@ -116,11 +119,15 @@ MetavoxelEditor::MetavoxelEditor() :
addTool(new RemoveSpannerTool(this));
addTool(new ClearSpannersTool(this));
addTool(new SetSpannerTool(this));
addTool(new ImportHeightfieldTool(this));
addTool(new EraseHeightfieldTool(this));
addTool(new HeightfieldHeightBrushTool(this));
addTool(new HeightfieldColorBrushTool(this));
addTool(new HeightfieldTextureBrushTool(this));
addTool(new HeightfieldMaterialBrushTool(this));
addTool(new ImportHeightfieldTool(this));
addTool(new EraseHeightfieldTool(this));
addTool(new VoxelColorBoxTool(this));
addTool(new VoxelMaterialBoxTool(this));
addTool(new VoxelColorSphereTool(this));
addTool(new VoxelMaterialSphereTool(this));
updateAttributes();
@ -200,7 +207,7 @@ void MetavoxelEditor::selectedAttributeChanged() {
AttributePointer attribute = AttributeRegistry::getInstance()->getAttribute(selected);
foreach (MetavoxelTool* tool, _tools) {
if (tool->appliesTo(attribute)) {
if (tool->appliesTo(attribute) && (tool->isUserFacing() || _showAll->isChecked())) {
_toolBox->addItem(tool->objectName(), QVariant::fromValue(tool));
}
}
@ -214,6 +221,7 @@ void MetavoxelEditor::selectedAttributeChanged() {
editor->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Preferred);
_valueArea->setWidget(editor);
}
updateTool();
}
void MetavoxelEditor::createNewAttribute() {
@ -271,6 +279,35 @@ void MetavoxelEditor::alignGridPosition() {
_gridPosition->setValue(step * floor(_gridPosition->value() / step));
}
void MetavoxelEditor::updateAttributes(const QString& select) {
// remember the selection in order to preserve it
QString selected = select.isNull() ? getSelectedAttribute() : select;
_attributes->clear();
// sort the names for consistent ordering
QList<QString> names;
if (_showAll->isChecked()) {
names = AttributeRegistry::getInstance()->getAttributes().keys();
} else {
foreach (const AttributePointer& attribute, AttributeRegistry::getInstance()->getAttributes()) {
if (attribute->isUserFacing()) {
names.append(attribute->getName());
}
}
}
qSort(names);
foreach (const QString& name, names) {
QListWidgetItem* item = new QListWidgetItem(name);
_attributes->addItem(item);
if (name == selected || selected.isNull()) {
item->setSelected(true);
selected = name;
}
}
}
void MetavoxelEditor::updateTool() {
MetavoxelTool* active = getActiveTool();
foreach (MetavoxelTool* tool, _tools) {
@ -335,25 +372,6 @@ void MetavoxelEditor::addTool(MetavoxelTool* tool) {
layout()->addWidget(tool);
}
void MetavoxelEditor::updateAttributes(const QString& select) {
// remember the selection in order to preserve it
QString selected = select.isNull() ? getSelectedAttribute() : select;
_attributes->clear();
// sort the names for consistent ordering
QList<QString> names = AttributeRegistry::getInstance()->getAttributes().keys();
qSort(names);
foreach (const QString& name, names) {
QListWidgetItem* item = new QListWidgetItem(name);
_attributes->addItem(item);
if (name == selected || selected.isNull()) {
item->setSelected(true);
selected = name;
}
}
}
MetavoxelTool* MetavoxelEditor::getActiveTool() const {
int index = _toolBox->currentIndex();
return (index == -1) ? NULL : static_cast<MetavoxelTool*>(_toolBox->itemData(index).value<QObject*>());
@ -361,9 +379,10 @@ MetavoxelTool* MetavoxelEditor::getActiveTool() const {
ProgramObject MetavoxelEditor::_gridProgram;
MetavoxelTool::MetavoxelTool(MetavoxelEditor* editor, const QString& name, bool usesValue) :
MetavoxelTool::MetavoxelTool(MetavoxelEditor* editor, const QString& name, bool usesValue, bool userFacing) :
_editor(editor),
_usesValue(usesValue) {
_usesValue(usesValue),
_userFacing(userFacing) {
QVBoxLayout* layout = new QVBoxLayout();
setLayout(layout);
@ -385,13 +404,13 @@ void MetavoxelTool::render() {
// nothing by default
}
BoxSetTool::BoxSetTool(MetavoxelEditor* editor) :
MetavoxelTool(editor, "Set Value (Box)") {
BoxTool::BoxTool(MetavoxelEditor* editor, const QString& name, bool usesValue, bool userFacing) :
MetavoxelTool(editor, name, usesValue, userFacing) {
resetState();
}
void BoxSetTool::render() {
void BoxTool::render() {
if (Application::getInstance()->isMouseHidden()) {
resetState();
return;
@ -457,7 +476,7 @@ void BoxSetTool::render() {
glTranslatef(0.5f, 0.5f, 0.5f);
if (_state != HOVERING_STATE) {
const float BOX_ALPHA = 0.25f;
QColor color = _editor->getValue().value<QColor>();
QColor color = getColor();
if (color.isValid()) {
glColor4f(color.redF(), color.greenF(), color.blueF(), BOX_ALPHA);
} else {
@ -476,7 +495,7 @@ void BoxSetTool::render() {
glPopMatrix();
}
bool BoxSetTool::eventFilter(QObject* watched, QEvent* event) {
bool BoxTool::eventFilter(QObject* watched, QEvent* event) {
switch (_state) {
case HOVERING_STATE:
if (event->type() == QEvent::MouseButtonPress && _startPosition != INVALID_VECTOR) {
@ -515,12 +534,20 @@ bool BoxSetTool::eventFilter(QObject* watched, QEvent* event) {
return false;
}
void BoxSetTool::resetState() {
void BoxTool::resetState() {
_state = HOVERING_STATE;
_startPosition = INVALID_VECTOR;
_height = 0.0f;
}
BoxSetTool::BoxSetTool(MetavoxelEditor* editor) :
BoxTool(editor, "Set Value (Box)", true, false) {
}
QColor BoxSetTool::getColor() {
return _editor->getValue().value<QColor>();
}
void BoxSetTool::applyValue(const glm::vec3& minimum, const glm::vec3& maximum) {
AttributePointer attribute = AttributeRegistry::getInstance()->getAttribute(_editor->getSelectedAttribute());
if (!attribute) {
@ -533,7 +560,7 @@ void BoxSetTool::applyValue(const glm::vec3& minimum, const glm::vec3& maximum)
}
GlobalSetTool::GlobalSetTool(MetavoxelEditor* editor) :
MetavoxelTool(editor, "Set Value (Global)") {
MetavoxelTool(editor, "Set Value (Global)", true, false) {
QPushButton* button = new QPushButton("Apply");
layout()->addWidget(button);
@ -944,11 +971,9 @@ ImportHeightfieldTool::ImportHeightfieldTool(MetavoxelEditor* editor) :
connect(_height, &QAbstractButton::clicked, this, &ImportHeightfieldTool::selectHeightFile);
_form->addRow("Color:", _color = new QPushButton());
connect(_color, &QAbstractButton::clicked, this, &ImportHeightfieldTool::selectColorFile);
}
void ImportHeightfieldTool::render() {
HeightfieldTool::render();
_preview.render(_translation->getValue(), _translation->getSingleStep());
connect(Application::getInstance()->getMetavoxels(), &MetavoxelSystem::rendering,
this, &ImportHeightfieldTool::renderPreview);
}
void ImportHeightfieldTool::apply() {
@ -966,7 +991,7 @@ void ImportHeightfieldTool::apply() {
QByteArray color;
if (buffer->getColor().isEmpty()) {
const int WHITE_VALUE = 0xFF;
color = QByteArray(height.size() * HeightfieldData::COLOR_BYTES, WHITE_VALUE);
color = QByteArray(height.size() * DataBlock::COLOR_BYTES, WHITE_VALUE);
} else {
color = buffer->getUnextendedColor();
}
@ -975,10 +1000,10 @@ void ImportHeightfieldTool::apply() {
AttributeRegistry::getInstance()->getHeightfieldColorAttribute(), encodeInline(colorPointer))));
int size = glm::sqrt(height.size()) + HeightfieldBuffer::SHARED_EDGE;
QByteArray texture(size * size, 0);
HeightfieldTextureDataPointer texturePointer(new HeightfieldTextureData(texture));
data.setRoot(AttributeRegistry::getInstance()->getHeightfieldTextureAttribute(), new MetavoxelNode(AttributeValue(
AttributeRegistry::getInstance()->getHeightfieldTextureAttribute(), encodeInline(texturePointer))));
QByteArray material(size * size, 0);
HeightfieldMaterialDataPointer materialPointer(new HeightfieldMaterialData(material));
data.setRoot(AttributeRegistry::getInstance()->getHeightfieldMaterialAttribute(), new MetavoxelNode(AttributeValue(
AttributeRegistry::getInstance()->getHeightfieldMaterialAttribute(), encodeInline(materialPointer))));
MetavoxelEditMessage message = { QVariant::fromValue(SetDataEdit(
_translation->getValue() + buffer->getTranslation() * scale, data)) };
@ -1032,22 +1057,22 @@ void ImportHeightfieldTool::updatePreview() {
int rows = qMin(heightSize - offsetY, _heightImage.height() - extendedI);
int columns = qMin(heightSize - offsetX, _heightImage.width() - extendedJ);
for (int y = 0; y < rows; y++) {
uchar* src = _heightImage.scanLine(extendedI + y) + extendedJ * HeightfieldData::COLOR_BYTES;
uchar* src = _heightImage.scanLine(extendedI + y) + extendedJ * DataBlock::COLOR_BYTES;
char* dest = height.data() + (y + offsetY) * heightSize + offsetX;
for (int x = 0; x < columns; x++) {
*dest++ = *src;
src += HeightfieldData::COLOR_BYTES;
src += DataBlock::COLOR_BYTES;
}
}
QByteArray color;
if (!_colorImage.isNull()) {
color = QByteArray(colorSize * colorSize * HeightfieldData::COLOR_BYTES, 0);
color = QByteArray(colorSize * colorSize * DataBlock::COLOR_BYTES, 0);
rows = qMax(0, qMin(colorSize, _colorImage.height() - i));
columns = qMax(0, qMin(colorSize, _colorImage.width() - j));
for (int y = 0; y < rows; y++) {
memcpy(color.data() + y * colorSize * HeightfieldData::COLOR_BYTES,
_colorImage.scanLine(i + y) + j * HeightfieldData::COLOR_BYTES,
columns * HeightfieldData::COLOR_BYTES);
memcpy(color.data() + y * colorSize * DataBlock::COLOR_BYTES,
_colorImage.scanLine(i + y) + j * DataBlock::COLOR_BYTES,
columns * DataBlock::COLOR_BYTES);
}
}
buffers.append(BufferDataPointer(new HeightfieldBuffer(glm::vec3(x, 0.0f, z), 1.0f, height, color)));
@ -1057,6 +1082,12 @@ void ImportHeightfieldTool::updatePreview() {
_preview.setBuffers(buffers);
}
void ImportHeightfieldTool::renderPreview() {
if (isVisible()) {
_preview.render(_translation->getValue(), _translation->getSingleStep());
}
}
EraseHeightfieldTool::EraseHeightfieldTool(MetavoxelEditor* editor) :
HeightfieldTool(editor, "Erase Heightfield") {
@ -1177,25 +1208,190 @@ QVariant HeightfieldColorBrushTool::createEdit(bool alternate) {
alternate ? QColor() : _color->getColor()));
}
HeightfieldTextureBrushTool::HeightfieldTextureBrushTool(MetavoxelEditor* editor) :
HeightfieldBrushTool(editor, "Texture Brush") {
HeightfieldMaterialBrushTool::HeightfieldMaterialBrushTool(MetavoxelEditor* editor) :
HeightfieldBrushTool(editor, "Material Brush") {
_form->addRow(_textureEditor = new SharedObjectEditor(&HeightfieldTexture::staticMetaObject, false));
connect(_textureEditor, &SharedObjectEditor::objectChanged, this, &HeightfieldTextureBrushTool::updateTexture);
_form->addRow(_materialEditor = new SharedObjectEditor(&MaterialObject::staticMetaObject, false));
connect(_materialEditor, &SharedObjectEditor::objectChanged, this, &HeightfieldMaterialBrushTool::updateTexture);
}
QVariant HeightfieldTextureBrushTool::createEdit(bool alternate) {
QVariant HeightfieldMaterialBrushTool::createEdit(bool alternate) {
if (alternate) {
return QVariant::fromValue(PaintHeightfieldTextureEdit(_position, _radius->value(), SharedObjectPointer(), QColor()));
return QVariant::fromValue(PaintHeightfieldMaterialEdit(_position, _radius->value(), SharedObjectPointer(), QColor()));
} else {
SharedObjectPointer texture = _textureEditor->getObject();
_textureEditor->detachObject();
return QVariant::fromValue(PaintHeightfieldTextureEdit(_position, _radius->value(), texture,
SharedObjectPointer material = _materialEditor->getObject();
_materialEditor->detachObject();
return QVariant::fromValue(PaintHeightfieldMaterialEdit(_position, _radius->value(), material,
_texture ? _texture->getAverageColor() : QColor()));
}
}
void HeightfieldTextureBrushTool::updateTexture() {
HeightfieldTexture* texture = static_cast<HeightfieldTexture*>(_textureEditor->getObject().data());
_texture = Application::getInstance()->getTextureCache()->getTexture(texture->getURL());
void HeightfieldMaterialBrushTool::updateTexture() {
MaterialObject* material = static_cast<MaterialObject*>(_materialEditor->getObject().data());
_texture = Application::getInstance()->getTextureCache()->getTexture(material->getDiffuse(), SPLAT_TEXTURE);
}
VoxelColorBoxTool::VoxelColorBoxTool(MetavoxelEditor* editor) :
BoxTool(editor, "Set Voxel Color (Box)", false) {
QWidget* widget = new QWidget();
QFormLayout* form = new QFormLayout();
widget->setLayout(form);
layout()->addWidget(widget);
form->addRow("Color:", _color = new QColorEditor(this));
}
bool VoxelColorBoxTool::appliesTo(const AttributePointer& attribute) const {
return attribute->inherits("VoxelColorAttribute");
}
QColor VoxelColorBoxTool::getColor() {
return _color->getColor();
}
void VoxelColorBoxTool::applyValue(const glm::vec3& minimum, const glm::vec3& maximum) {
MetavoxelEditMessage message = { QVariant::fromValue(VoxelColorBoxEdit(Box(minimum, maximum),
_editor->getGridSpacing(), _color->getColor())) };
Application::getInstance()->getMetavoxels()->applyEdit(message, true);
}
VoxelMaterialBoxTool::VoxelMaterialBoxTool(MetavoxelEditor* editor) :
BoxTool(editor, "Set Voxel Material (Box)", false) {
QWidget* widget = new QWidget();
QFormLayout* form = new QFormLayout();
widget->setLayout(form);
layout()->addWidget(widget);
form->addRow(_materialEditor = new SharedObjectEditor(&MaterialObject::staticMetaObject, false));
connect(_materialEditor, &SharedObjectEditor::objectChanged, this, &VoxelMaterialBoxTool::updateTexture);
}
bool VoxelMaterialBoxTool::appliesTo(const AttributePointer& attribute) const {
return attribute->inherits("VoxelColorAttribute");
}
QColor VoxelMaterialBoxTool::getColor() {
return _texture ? _texture->getAverageColor() : QColor();
}
void VoxelMaterialBoxTool::applyValue(const glm::vec3& minimum, const glm::vec3& maximum) {
SharedObjectPointer material = _materialEditor->getObject();
_materialEditor->detachObject();
MetavoxelEditMessage message = { QVariant::fromValue(VoxelMaterialBoxEdit(Box(minimum, maximum),
_editor->getGridSpacing(), material, _texture ? _texture->getAverageColor() : QColor())) };
Application::getInstance()->getMetavoxels()->applyEdit(message, true);
}
void VoxelMaterialBoxTool::updateTexture() {
MaterialObject* material = static_cast<MaterialObject*>(_materialEditor->getObject().data());
_texture = Application::getInstance()->getTextureCache()->getTexture(material->getDiffuse(), SPLAT_TEXTURE);
}
SphereTool::SphereTool(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(1.0);
}
void SphereTool::render() {
if (Application::getInstance()->isMouseHidden()) {
return;
}
glm::quat rotation = _editor->getGridRotation();
glm::quat inverseRotation = glm::inverse(rotation);
glm::vec3 rayOrigin = inverseRotation * Application::getInstance()->getMouseRayOrigin();
glm::vec3 rayDirection = inverseRotation * Application::getInstance()->getMouseRayDirection();
float position = _editor->getGridPosition();
if (glm::abs(rayDirection.z) < EPSILON) {
return;
}
float distance = (position - rayOrigin.z) / rayDirection.z;
_position = Application::getInstance()->getMouseRayOrigin() +
Application::getInstance()->getMouseRayDirection() * distance;
glPushMatrix();
glTranslatef(_position.x, _position.y, _position.z);
const float CURSOR_ALPHA = 0.5f;
QColor color = getColor();
glColor4f(color.redF(), color.greenF(), color.blueF(), CURSOR_ALPHA);
glEnable(GL_CULL_FACE);
glutSolidSphere(_radius->value(), 10, 10);
glDisable(GL_CULL_FACE);
glPopMatrix();
}
bool SphereTool::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) {
applyValue(_position, _radius->value());
return true;
}
return false;
}
VoxelColorSphereTool::VoxelColorSphereTool(MetavoxelEditor* editor) :
SphereTool(editor, "Set Voxel Color (Sphere)") {
_form->addRow("Color:", _color = new QColorEditor(this));
}
bool VoxelColorSphereTool::appliesTo(const AttributePointer& attribute) const {
return attribute->inherits("VoxelColorAttribute");
}
QColor VoxelColorSphereTool::getColor() {
return _color->getColor();
}
void VoxelColorSphereTool::applyValue(const glm::vec3& position, float radius) {
MetavoxelEditMessage message = { QVariant::fromValue(VoxelColorSphereEdit(position, radius,
_editor->getGridSpacing(), _color->getColor())) };
Application::getInstance()->getMetavoxels()->applyEdit(message, true);
}
VoxelMaterialSphereTool::VoxelMaterialSphereTool(MetavoxelEditor* editor) :
SphereTool(editor, "Set Voxel Material (Sphere)") {
_form->addRow(_materialEditor = new SharedObjectEditor(&MaterialObject::staticMetaObject, false));
connect(_materialEditor, &SharedObjectEditor::objectChanged, this, &VoxelMaterialSphereTool::updateTexture);
}
bool VoxelMaterialSphereTool::appliesTo(const AttributePointer& attribute) const {
return attribute->inherits("VoxelColorAttribute");
}
QColor VoxelMaterialSphereTool::getColor() {
return _texture ? _texture->getAverageColor() : QColor();
}
void VoxelMaterialSphereTool::applyValue(const glm::vec3& position, float radius) {
SharedObjectPointer material = _materialEditor->getObject();
_materialEditor->detachObject();
MetavoxelEditMessage message = { QVariant::fromValue(VoxelMaterialSphereEdit(position, radius,
_editor->getGridSpacing(), material, _texture ? _texture->getAverageColor() : QColor())) };
Application::getInstance()->getMetavoxels()->applyEdit(message, true);
}
void VoxelMaterialSphereTool::updateTexture() {
MaterialObject* material = static_cast<MaterialObject*>(_materialEditor->getObject().data());
_texture = Application::getInstance()->getTextureCache()->getTexture(material->getDiffuse(), SPLAT_TEXTURE);
}

View file

@ -57,6 +57,7 @@ private slots:
void deleteSelectedAttribute();
void centerGridPosition();
void alignGridPosition();
void updateAttributes(const QString& select = QString());
void updateTool();
void simulate(float deltaTime);
@ -65,11 +66,11 @@ private slots:
private:
void addTool(MetavoxelTool* tool);
void updateAttributes(const QString& select = QString());
MetavoxelTool* getActiveTool() const;
QListWidget* _attributes;
QPushButton* _deleteAttribute;
QCheckBox* _showAll;
QComboBox* _gridPlane;
QDoubleSpinBox* _gridSpacing;
@ -90,10 +91,12 @@ class MetavoxelTool : public QWidget {
public:
MetavoxelTool(MetavoxelEditor* editor, const QString& name, bool usesValue = true);
MetavoxelTool(MetavoxelEditor* editor, const QString& name, bool usesValue = true, bool userFacing = true);
bool getUsesValue() const { return _usesValue; }
bool isUserFacing() const { return _userFacing; }
virtual bool appliesTo(const AttributePointer& attribute) const;
virtual void simulate(float deltaTime);
@ -105,24 +108,30 @@ protected:
MetavoxelEditor* _editor;
bool _usesValue;
bool _userFacing;
};
/// Allows setting the value of a region by dragging out a box.
class BoxSetTool : public MetavoxelTool {
/// Base class for tools that allow dragging out a 3D box.
class BoxTool : public MetavoxelTool {
Q_OBJECT
public:
BoxSetTool(MetavoxelEditor* editor);
BoxTool(MetavoxelEditor* editor, const QString& name, bool usesValue = true, bool userFacing = true);
virtual void render();
virtual bool eventFilter(QObject* watched, QEvent* event);
protected:
virtual QColor getColor() = 0;
virtual void applyValue(const glm::vec3& minimum, const glm::vec3& maximum) = 0;
private:
void resetState();
void applyValue(const glm::vec3& minimum, const glm::vec3& maximum);
enum State { HOVERING_STATE, DRAGGING_STATE, RAISING_STATE };
@ -134,6 +143,21 @@ private:
float _height; ///< the selection height
};
/// Allows setting the value of a region by dragging out a box.
class BoxSetTool : public BoxTool {
Q_OBJECT
public:
BoxSetTool(MetavoxelEditor* editor);
protected:
virtual QColor getColor();
virtual void applyValue(const glm::vec3& minimum, const glm::vec3& maximum);
};
/// Allows setting the value across the entire space.
class GlobalSetTool : public MetavoxelTool {
Q_OBJECT
@ -259,8 +283,6 @@ public:
ImportHeightfieldTool(MetavoxelEditor* editor);
virtual void render();
protected:
virtual void apply();
@ -270,6 +292,7 @@ private slots:
void selectHeightFile();
void selectColorFile();
void updatePreview();
void renderPreview();
private:
@ -363,12 +386,12 @@ private:
};
/// Allows texturing parts of the heightfield.
class HeightfieldTextureBrushTool : public HeightfieldBrushTool {
class HeightfieldMaterialBrushTool : public HeightfieldBrushTool {
Q_OBJECT
public:
HeightfieldTextureBrushTool(MetavoxelEditor* editor);
HeightfieldMaterialBrushTool(MetavoxelEditor* editor);
protected:
@ -380,7 +403,125 @@ private slots:
private:
SharedObjectEditor* _textureEditor;
SharedObjectEditor* _materialEditor;
QSharedPointer<NetworkTexture> _texture;
};
/// Allows setting voxel colors by dragging out a box.
class VoxelColorBoxTool : public BoxTool {
Q_OBJECT
public:
VoxelColorBoxTool(MetavoxelEditor* editor);
virtual bool appliesTo(const AttributePointer& attribute) const;
protected:
virtual QColor getColor();
virtual void applyValue(const glm::vec3& minimum, const glm::vec3& maximum);
private:
QColorEditor* _color;
};
/// Allows setting voxel materials by dragging out a box.
class VoxelMaterialBoxTool : public BoxTool {
Q_OBJECT
public:
VoxelMaterialBoxTool(MetavoxelEditor* editor);
virtual bool appliesTo(const AttributePointer& attribute) const;
protected:
virtual QColor getColor();
virtual void applyValue(const glm::vec3& minimum, const glm::vec3& maximum);
private slots:
void updateTexture();
private:
SharedObjectEditor* _materialEditor;
QSharedPointer<NetworkTexture> _texture;
};
/// Base class for tools based on a sphere brush.
class SphereTool : public MetavoxelTool {
Q_OBJECT
public:
SphereTool(MetavoxelEditor* editor, const QString& name);
virtual void render();
virtual bool eventFilter(QObject* watched, QEvent* event);
protected:
virtual QColor getColor() = 0;
virtual void applyValue(const glm::vec3& position, float radius) = 0;
QFormLayout* _form;
QDoubleSpinBox* _radius;
glm::vec3 _position;
};
/// Allows setting voxel colors by moving a sphere around.
class VoxelColorSphereTool : public SphereTool {
Q_OBJECT
public:
VoxelColorSphereTool(MetavoxelEditor* editor);
virtual bool appliesTo(const AttributePointer& attribute) const;
protected:
virtual QColor getColor();
virtual void applyValue(const glm::vec3& position, float radius);
private:
QColorEditor* _color;
};
/// Allows setting voxel materials by moving a sphere around.
class VoxelMaterialSphereTool : public SphereTool {
Q_OBJECT
public:
VoxelMaterialSphereTool(MetavoxelEditor* editor);
virtual bool appliesTo(const AttributePointer& attribute) const;
protected:
virtual QColor getColor();
virtual void applyValue(const glm::vec3& position, float radius);
private slots:
void updateTexture();
private:
SharedObjectEditor* _materialEditor;
QSharedPointer<NetworkTexture> _texture;
};

File diff suppressed because it is too large Load diff

View file

@ -29,14 +29,17 @@ class QScriptEngine;
class QScriptValue;
class Attribute;
class DataBlock;
class HeightfieldColorData;
class HeightfieldData;
class HeightfieldHeightData;
class HeightfieldTextureData;
class HeightfieldMaterialData;
class MetavoxelData;
class MetavoxelLOD;
class MetavoxelNode;
class MetavoxelStreamState;
class VoxelColorData;
class VoxelHermiteData;
class VoxelMaterialData;
typedef SharedObjectPointerTemplate<Attribute> AttributePointer;
@ -100,14 +103,23 @@ public:
/// Returns a reference to the standard "spannerMask" attribute.
const AttributePointer& getSpannerMaskAttribute() const { return _spannerMaskAttribute; }
/// Returns a reference to the standard HeightfieldDataPointer "heightfield" attribute.
/// Returns a reference to the standard HeightfieldHeightDataPointer "heightfield" attribute.
const AttributePointer& getHeightfieldAttribute() const { return _heightfieldAttribute; }
/// Returns a reference to the standard HeightfieldDataPointer "heightfieldColor" attribute.
/// Returns a reference to the standard HeightfieldColorDataPointer "heightfieldColor" attribute.
const AttributePointer& getHeightfieldColorAttribute() const { return _heightfieldColorAttribute; }
/// Returns a reference to the standard HeightfieldDataPointer "heightfieldTexture" attribute.
const AttributePointer& getHeightfieldTextureAttribute() const { return _heightfieldTextureAttribute; }
/// Returns a reference to the standard HeightfieldMaterialDataPointer "heightfieldMaterial" attribute.
const AttributePointer& getHeightfieldMaterialAttribute() const { return _heightfieldMaterialAttribute; }
/// Returns a reference to the standard VoxelColorDataPointer "voxelColor" attribute.
const AttributePointer& getVoxelColorAttribute() const { return _voxelColorAttribute; }
/// Returns a reference to the standard VoxelMaterialDataPointer "voxelMaterial" attribute.
const AttributePointer& getVoxelMaterialAttribute() const { return _voxelMaterialAttribute; }
/// Returns a reference to the standard VoxelHermiteDataPointer "voxelHermite" attribute.
const AttributePointer& getVoxelHermiteAttribute() const { return _voxelHermiteAttribute; }
private:
@ -126,7 +138,10 @@ private:
AttributePointer _spannerMaskAttribute;
AttributePointer _heightfieldAttribute;
AttributePointer _heightfieldColorAttribute;
AttributePointer _heightfieldTextureAttribute;
AttributePointer _heightfieldMaterialAttribute;
AttributePointer _voxelColorAttribute;
AttributePointer _voxelMaterialAttribute;
AttributePointer _voxelHermiteAttribute;
};
/// Converts a value to a void pointer.
@ -206,6 +221,7 @@ Q_DECLARE_METATYPE(OwnedAttributeValue)
class Attribute : public SharedObject {
Q_OBJECT
Q_PROPERTY(float lodThresholdMultiplier MEMBER _lodThresholdMultiplier)
Q_PROPERTY(bool userFacing MEMBER _userFacing)
public:
@ -219,6 +235,9 @@ public:
float getLODThresholdMultiplier() const { return _lodThresholdMultiplier; }
void setLODThresholdMultiplier(float multiplier) { _lodThresholdMultiplier = multiplier; }
bool isUserFacing() const { return _userFacing; }
void setUserFacing(bool userFacing) { _userFacing = userFacing; }
void* create() const { return create(getDefaultValue()); }
virtual void* create(void* copy) const = 0;
virtual void destroy(void* value) const = 0;
@ -283,6 +302,7 @@ public:
private:
float _lodThresholdMultiplier;
bool _userFacing;
};
/// A simple attribute class that stores its values inline.
@ -396,6 +416,9 @@ public:
/// Packs a normal into an RGB value.
QRgb packNormal(const glm::vec3& normal);
/// Packs a normal (plus extra alpha value) into an RGBA value.
QRgb packNormal(const glm::vec3& normal, int alpha);
/// Unpacks a normal from an RGB value.
glm::vec3 unpackNormal(QRgb value);
@ -435,21 +458,18 @@ public:
virtual AttributeValue inherit(const AttributeValue& parentValue) const;
};
typedef QExplicitlySharedDataPointer<HeightfieldData> HeightfieldDataPointer;
typedef QExplicitlySharedDataPointer<DataBlock> DataBlockPointer;
/// Contains a block of heightfield data.
class HeightfieldData : public QSharedData {
/// Base class for blocks of data.
class DataBlock : public QSharedData {
public:
static const int COLOR_BYTES = 3;
HeightfieldData(const QByteArray& contents = QByteArray());
virtual ~HeightfieldData();
virtual ~DataBlock();
const QByteArray& getContents() const { return _contents; }
void setDeltaData(const HeightfieldDataPointer& deltaData) { _deltaData = deltaData; }
const HeightfieldDataPointer& getDeltaData() const { return _deltaData; }
void setDeltaData(const DataBlockPointer& deltaData) { _deltaData = deltaData; }
const DataBlockPointer& getDeltaData() const { return _deltaData; }
void setEncodedDelta(const QByteArray& encodedDelta) { _encodedDelta = encodedDelta; }
const QByteArray& getEncodedDelta() const { return _encodedDelta; }
@ -458,17 +478,16 @@ public:
protected:
QByteArray _contents;
QByteArray _encoded;
QMutex _encodedMutex;
HeightfieldDataPointer _deltaData;
DataBlockPointer _deltaData;
QByteArray _encodedDelta;
QMutex _encodedDeltaMutex;
class EncodedSubdivision {
public:
HeightfieldDataPointer ancestor;
DataBlockPointer ancestor;
QByteArray data;
};
QVector<EncodedSubdivision> _encodedSubdivisions;
@ -478,7 +497,7 @@ protected:
typedef QExplicitlySharedDataPointer<HeightfieldHeightData> HeightfieldHeightDataPointer;
/// Contains a block of heightfield height data.
class HeightfieldHeightData : public HeightfieldData {
class HeightfieldHeightData : public DataBlock {
public:
HeightfieldHeightData(const QByteArray& contents);
@ -487,6 +506,8 @@ public:
HeightfieldHeightData(Bitstream& in, int bytes, const HeightfieldHeightDataPointer& ancestor,
const glm::vec3& minimum, float size);
const QByteArray& getContents() const { return _contents; }
void write(Bitstream& out);
void writeDelta(Bitstream& out, const HeightfieldHeightDataPointer& reference);
void writeSubdivided(Bitstream& out, const HeightfieldHeightDataPointer& ancestor,
@ -496,75 +517,8 @@ private:
void read(Bitstream& in, int bytes);
void set(const QImage& image);
};
typedef QExplicitlySharedDataPointer<HeightfieldColorData> HeightfieldColorDataPointer;
/// Contains a block of heightfield color data.
class HeightfieldColorData : public HeightfieldData {
public:
HeightfieldColorData(const QByteArray& contents);
HeightfieldColorData(Bitstream& in, int bytes);
HeightfieldColorData(Bitstream& in, int bytes, const HeightfieldColorDataPointer& reference);
HeightfieldColorData(Bitstream& in, int bytes, const HeightfieldColorDataPointer& ancestor,
const glm::vec3& minimum, float size);
void write(Bitstream& out);
void writeDelta(Bitstream& out, const HeightfieldColorDataPointer& reference);
void writeSubdivided(Bitstream& out, const HeightfieldColorDataPointer& ancestor,
const glm::vec3& minimum, float size);
private:
void read(Bitstream& in, int bytes);
void set(const QImage& image);
};
typedef QExplicitlySharedDataPointer<HeightfieldTextureData> HeightfieldTextureDataPointer;
/// Contains a block of heightfield texture data.
class HeightfieldTextureData : public HeightfieldData {
public:
HeightfieldTextureData(const QByteArray& contents,
const QVector<SharedObjectPointer>& textures = QVector<SharedObjectPointer>());
HeightfieldTextureData(Bitstream& in, int bytes);
HeightfieldTextureData(Bitstream& in, int bytes, const HeightfieldTextureDataPointer& reference);
const QVector<SharedObjectPointer>& getTextures() const { return _textures; }
void write(Bitstream& out);
void writeDelta(Bitstream& out, const HeightfieldTextureDataPointer& reference);
private:
void read(Bitstream& in, int bytes);
QVector<SharedObjectPointer> _textures;
};
/// Contains the description of a heightfield texture.
class HeightfieldTexture : public SharedObject {
Q_OBJECT
Q_PROPERTY(QUrl url MEMBER _url)
Q_PROPERTY(float scaleS MEMBER _scaleS)
Q_PROPERTY(float scaleT MEMBER _scaleT)
public:
Q_INVOKABLE HeightfieldTexture();
const QUrl& getURL() const { return _url; }
float getScaleS() const { return _scaleS; }
float getScaleT() const { return _scaleT; }
private:
QUrl _url;
float _scaleS;
float _scaleT;
QByteArray _contents;
};
/// An attribute that stores heightfield data.
@ -584,6 +538,33 @@ public:
virtual bool merge(void*& parent, void* children[], bool postRead = false) const;
};
typedef QExplicitlySharedDataPointer<HeightfieldColorData> HeightfieldColorDataPointer;
/// Contains a block of heightfield color data.
class HeightfieldColorData : public DataBlock {
public:
HeightfieldColorData(const QByteArray& contents);
HeightfieldColorData(Bitstream& in, int bytes);
HeightfieldColorData(Bitstream& in, int bytes, const HeightfieldColorDataPointer& reference);
HeightfieldColorData(Bitstream& in, int bytes, const HeightfieldColorDataPointer& ancestor,
const glm::vec3& minimum, float size);
const QByteArray& getContents() const { return _contents; }
void write(Bitstream& out);
void writeDelta(Bitstream& out, const HeightfieldColorDataPointer& reference);
void writeSubdivided(Bitstream& out, const HeightfieldColorDataPointer& ancestor,
const glm::vec3& minimum, float size);
private:
void read(Bitstream& in, int bytes);
void set(const QImage& image);
QByteArray _contents;
};
/// An attribute that stores heightfield colors.
class HeightfieldColorAttribute : public InlineAttribute<HeightfieldColorDataPointer> {
Q_OBJECT
@ -601,13 +582,194 @@ public:
virtual bool merge(void*& parent, void* children[], bool postRead = false) const;
};
/// An attribute that stores heightfield textures.
class HeightfieldTextureAttribute : public InlineAttribute<HeightfieldTextureDataPointer> {
typedef QExplicitlySharedDataPointer<HeightfieldMaterialData> HeightfieldMaterialDataPointer;
/// Contains a block of heightfield material data.
class HeightfieldMaterialData : public DataBlock {
public:
HeightfieldMaterialData(const QByteArray& contents,
const QVector<SharedObjectPointer>& materials = QVector<SharedObjectPointer>());
HeightfieldMaterialData(Bitstream& in, int bytes);
HeightfieldMaterialData(Bitstream& in, int bytes, const HeightfieldMaterialDataPointer& reference);
const QByteArray& getContents() const { return _contents; }
const QVector<SharedObjectPointer>& getMaterials() const { return _materials; }
void write(Bitstream& out);
void writeDelta(Bitstream& out, const HeightfieldMaterialDataPointer& reference);
private:
void read(Bitstream& in, int bytes);
QByteArray _contents;
QVector<SharedObjectPointer> _materials;
};
/// Contains the description of a material.
class MaterialObject : public SharedObject {
Q_OBJECT
Q_PROPERTY(QUrl diffuse MEMBER _diffuse)
Q_PROPERTY(float scaleS MEMBER _scaleS)
Q_PROPERTY(float scaleT MEMBER _scaleT)
public:
Q_INVOKABLE MaterialObject();
const QUrl& getDiffuse() const { return _diffuse; }
float getScaleS() const { return _scaleS; }
float getScaleT() const { return _scaleT; }
private:
QUrl _diffuse;
float _scaleS;
float _scaleT;
};
/// An attribute that stores heightfield materials.
class HeightfieldMaterialAttribute : public InlineAttribute<HeightfieldMaterialDataPointer> {
Q_OBJECT
public:
Q_INVOKABLE HeightfieldTextureAttribute(const QString& name = QString());
Q_INVOKABLE HeightfieldMaterialAttribute(const QString& name = QString());
virtual void read(Bitstream& in, void*& value, bool isLeaf) const;
virtual void write(Bitstream& out, void* value, bool isLeaf) const;
virtual void readDelta(Bitstream& in, void*& value, void* reference, bool isLeaf) const;
virtual void writeDelta(Bitstream& out, void* value, void* reference, bool isLeaf) const;
virtual bool merge(void*& parent, void* children[], bool postRead = false) const;
};
typedef QExplicitlySharedDataPointer<VoxelColorData> VoxelColorDataPointer;
/// Contains a block of voxel color data.
class VoxelColorData : public DataBlock {
public:
VoxelColorData(const QVector<QRgb>& contents, int size);
VoxelColorData(Bitstream& in, int bytes);
VoxelColorData(Bitstream& in, int bytes, const VoxelColorDataPointer& reference);
const QVector<QRgb>& getContents() const { return _contents; }
int getSize() const { return _size; }
void write(Bitstream& out);
void writeDelta(Bitstream& out, const VoxelColorDataPointer& reference);
private:
void read(Bitstream& in, int bytes);
QVector<QRgb> _contents;
int _size;
};
/// An attribute that stores voxel colors.
class VoxelColorAttribute : public InlineAttribute<VoxelColorDataPointer> {
Q_OBJECT
public:
Q_INVOKABLE VoxelColorAttribute(const QString& name = QString());
virtual void read(Bitstream& in, void*& value, bool isLeaf) const;
virtual void write(Bitstream& out, void* value, bool isLeaf) const;
virtual void readDelta(Bitstream& in, void*& value, void* reference, bool isLeaf) const;
virtual void writeDelta(Bitstream& out, void* value, void* reference, bool isLeaf) const;
virtual bool merge(void*& parent, void* children[], bool postRead = false) const;
};
typedef QExplicitlySharedDataPointer<VoxelMaterialData> VoxelMaterialDataPointer;
/// Contains a block of voxel material data.
class VoxelMaterialData : public DataBlock {
public:
VoxelMaterialData(const QByteArray& contents, int size,
const QVector<SharedObjectPointer>& materials = QVector<SharedObjectPointer>());
VoxelMaterialData(Bitstream& in, int bytes);
VoxelMaterialData(Bitstream& in, int bytes, const VoxelMaterialDataPointer& reference);
const QByteArray& getContents() const { return _contents; }
int getSize() const { return _size; }
const QVector<SharedObjectPointer>& getMaterials() const { return _materials; }
void write(Bitstream& out);
void writeDelta(Bitstream& out, const VoxelMaterialDataPointer& reference);
private:
void read(Bitstream& in, int bytes);
QByteArray _contents;
int _size;
QVector<SharedObjectPointer> _materials;
};
/// An attribute that stores voxel materials.
class VoxelMaterialAttribute : public InlineAttribute<VoxelMaterialDataPointer> {
Q_OBJECT
public:
Q_INVOKABLE VoxelMaterialAttribute(const QString& name = QString());
virtual void read(Bitstream& in, void*& value, bool isLeaf) const;
virtual void write(Bitstream& out, void* value, bool isLeaf) const;
virtual void readDelta(Bitstream& in, void*& value, void* reference, bool isLeaf) const;
virtual void writeDelta(Bitstream& out, void* value, void* reference, bool isLeaf) const;
virtual bool merge(void*& parent, void* children[], bool postRead = false) const;
};
typedef QExplicitlySharedDataPointer<VoxelHermiteData> VoxelHermiteDataPointer;
/// Contains a block of voxel Hermite data (positions and normals at edge crossings).
class VoxelHermiteData : public DataBlock {
public:
static const int EDGE_COUNT = 3;
VoxelHermiteData(const QVector<QRgb>& contents, int size);
VoxelHermiteData(Bitstream& in, int bytes);
VoxelHermiteData(Bitstream& in, int bytes, const VoxelHermiteDataPointer& reference);
const QVector<QRgb>& getContents() const { return _contents; }
int getSize() const { return _size; }
void write(Bitstream& out);
void writeDelta(Bitstream& out, const VoxelHermiteDataPointer& reference);
private:
void read(Bitstream& in, int bytes);
QVector<QRgb> _contents;
int _size;
};
/// An attribute that stores voxel Hermite data.
class VoxelHermiteAttribute : public InlineAttribute<VoxelHermiteDataPointer> {
Q_OBJECT
public:
Q_INVOKABLE VoxelHermiteAttribute(const QString& name = QString());
virtual void read(Bitstream& in, void*& value, bool isLeaf) const;
virtual void write(Bitstream& out, void* value, bool isLeaf) const;

View file

@ -414,119 +414,36 @@ PaintHeightfieldColorEdit::PaintHeightfieldColorEdit(const glm::vec3& position,
color(color) {
}
class PaintHeightfieldColorEditVisitor : public MetavoxelVisitor {
class PaintHeightfieldMaterialEditVisitor : public MetavoxelVisitor {
public:
PaintHeightfieldColorEditVisitor(const PaintHeightfieldColorEdit& edit);
PaintHeightfieldMaterialEditVisitor(const glm::vec3& position, float radius,
const SharedObjectPointer& material, const QColor& color);
virtual int visit(MetavoxelInfo& info);
private:
PaintHeightfieldColorEdit _edit;
glm::vec3 _position;
float _radius;
SharedObjectPointer _material;
QColor _color;
Box _bounds;
};
PaintHeightfieldColorEditVisitor::PaintHeightfieldColorEditVisitor(const PaintHeightfieldColorEdit& edit) :
MetavoxelVisitor(QVector<AttributePointer>() << AttributeRegistry::getInstance()->getHeightfieldColorAttribute(),
QVector<AttributePointer>() << AttributeRegistry::getInstance()->getHeightfieldColorAttribute()),
_edit(edit) {
PaintHeightfieldMaterialEditVisitor::PaintHeightfieldMaterialEditVisitor(const glm::vec3& position, float radius,
const SharedObjectPointer& material, const QColor& color) :
MetavoxelVisitor(QVector<AttributePointer>() << AttributeRegistry::getInstance()->getHeightfieldColorAttribute() <<
AttributeRegistry::getInstance()->getHeightfieldMaterialAttribute(), QVector<AttributePointer>() <<
AttributeRegistry::getInstance()->getHeightfieldColorAttribute() <<
AttributeRegistry::getInstance()->getHeightfieldMaterialAttribute()),
_position(position),
_radius(radius),
_material(material),
_color(color) {
glm::vec3 extents(_edit.radius, _edit.radius, _edit.radius);
_bounds = Box(_edit.position - extents, _edit.position + extents);
}
static void paintColor(MetavoxelInfo& info, int index, const glm::vec3& position, float radius, const QColor& color) {
HeightfieldColorDataPointer pointer = info.inputValues.at(index).getInlineValue<HeightfieldColorDataPointer>();
if (!pointer) {
return;
}
QByteArray contents(pointer->getContents());
int size = glm::sqrt((float)contents.size() / HeightfieldData::COLOR_BYTES);
int highest = size - 1;
float heightScale = size / info.size;
glm::vec3 center = (position - info.minimum) * heightScale;
float scaledRadius = radius * heightScale;
glm::vec3 extents(scaledRadius, scaledRadius, scaledRadius);
glm::vec3 start = glm::floor(center - extents);
glm::vec3 end = glm::ceil(center + extents);
// paint all points within the radius
float z = qMax(start.z, 0.0f);
float startX = qMax(start.x, 0.0f), endX = qMin(end.x, (float)highest);
int stride = size * HeightfieldData::COLOR_BYTES;
char* lineDest = contents.data() + (int)z * stride + (int)startX * HeightfieldData::COLOR_BYTES;
float squaredRadius = scaledRadius * scaledRadius;
char red = color.red(), green = color.green(), blue = color.blue();
bool changed = false;
for (float endZ = qMin(end.z, (float)highest); z <= endZ; z += 1.0f) {
char* dest = lineDest;
for (float x = startX; x <= endX; x += 1.0f, dest += HeightfieldData::COLOR_BYTES) {
float dx = x - center.x, dz = z - center.z;
if (dx * dx + dz * dz <= squaredRadius) {
dest[0] = red;
dest[1] = green;
dest[2] = blue;
changed = true;
}
}
lineDest += stride;
}
if (changed) {
HeightfieldColorDataPointer newPointer(new HeightfieldColorData(contents));
info.outputValues[index] = AttributeValue(info.inputValues.at(index).getAttribute(),
encodeInline<HeightfieldColorDataPointer>(newPointer));
}
}
int PaintHeightfieldColorEditVisitor::visit(MetavoxelInfo& info) {
if (!info.getBounds().intersects(_bounds)) {
return STOP_RECURSION;
}
if (!info.isLeaf) {
return DEFAULT_ORDER;
}
paintColor(info, 0, _edit.position, _edit.radius, _edit.color);
return STOP_RECURSION;
}
void PaintHeightfieldColorEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const {
PaintHeightfieldColorEditVisitor visitor(*this);
data.guide(visitor);
}
PaintHeightfieldTextureEdit::PaintHeightfieldTextureEdit(const glm::vec3& position, float radius,
const SharedObjectPointer& texture, const QColor& averageColor) :
position(position),
radius(radius),
texture(texture),
averageColor(averageColor) {
}
class PaintHeightfieldTextureEditVisitor : public MetavoxelVisitor {
public:
PaintHeightfieldTextureEditVisitor(const PaintHeightfieldTextureEdit& edit);
virtual int visit(MetavoxelInfo& info);
private:
PaintHeightfieldTextureEdit _edit;
Box _bounds;
};
PaintHeightfieldTextureEditVisitor::PaintHeightfieldTextureEditVisitor(const PaintHeightfieldTextureEdit& edit) :
MetavoxelVisitor(QVector<AttributePointer>() << AttributeRegistry::getInstance()->getHeightfieldTextureAttribute() <<
AttributeRegistry::getInstance()->getHeightfieldColorAttribute(), QVector<AttributePointer>() <<
AttributeRegistry::getInstance()->getHeightfieldTextureAttribute() <<
AttributeRegistry::getInstance()->getHeightfieldColorAttribute()),
_edit(edit) {
glm::vec3 extents(_edit.radius, _edit.radius, _edit.radius);
_bounds = Box(_edit.position - extents, _edit.position + extents);
glm::vec3 extents(_radius, _radius, _radius);
_bounds = Box(_position - extents, _position + extents);
}
static QHash<uchar, int> countIndices(const QByteArray& contents) {
@ -539,106 +456,624 @@ static QHash<uchar, int> countIndices(const QByteArray& contents) {
return counts;
}
int PaintHeightfieldTextureEditVisitor::visit(MetavoxelInfo& info) {
uchar getMaterialIndex(const SharedObjectPointer& material, QVector<SharedObjectPointer>& materials, QByteArray& contents) {
if (!(material && static_cast<MaterialObject*>(material.data())->getDiffuse().isValid())) {
return 0;
}
// first look for a matching existing material, noting the first reusable slot
int firstEmptyIndex = -1;
for (int i = 0; i < materials.size(); i++) {
const SharedObjectPointer& existingMaterial = materials.at(i);
if (existingMaterial) {
if (existingMaterial->equals(material.data())) {
return i + 1;
}
} else if (firstEmptyIndex == -1) {
firstEmptyIndex = i;
}
}
// if nothing found, use the first empty slot or append
if (firstEmptyIndex != -1) {
materials[firstEmptyIndex] = material;
return firstEmptyIndex + 1;
}
if (materials.size() < EIGHT_BIT_MAXIMUM) {
materials.append(material);
return materials.size();
}
// last resort: find the least-used material and remove it
QHash<uchar, int> counts = countIndices(contents);
uchar materialIndex = 0;
int lowestCount = INT_MAX;
for (QHash<uchar, int>::const_iterator it = counts.constBegin(); it != counts.constEnd(); it++) {
if (it.value() < lowestCount) {
materialIndex = it.key();
lowestCount = it.value();
}
}
contents.replace((char)materialIndex, (char)0);
return materialIndex;
}
void clearUnusedMaterials(QVector<SharedObjectPointer>& materials, QByteArray& contents) {
QHash<uchar, int> counts = countIndices(contents);
for (int i = 0; i < materials.size(); i++) {
if (counts.value(i + 1) == 0) {
materials[i] = SharedObjectPointer();
}
}
while (!(materials.isEmpty() || materials.last())) {
materials.removeLast();
}
}
int PaintHeightfieldMaterialEditVisitor::visit(MetavoxelInfo& info) {
if (!info.getBounds().intersects(_bounds)) {
return STOP_RECURSION;
}
if (!info.isLeaf) {
return DEFAULT_ORDER;
}
HeightfieldTextureDataPointer pointer = info.inputValues.at(0).getInlineValue<HeightfieldTextureDataPointer>();
if (!pointer) {
return STOP_RECURSION;
}
QVector<SharedObjectPointer> textures = pointer->getTextures();
QByteArray contents(pointer->getContents());
uchar textureIndex = 0;
if (_edit.texture && static_cast<HeightfieldTexture*>(_edit.texture.data())->getURL().isValid()) {
// first look for a matching existing texture, noting the first reusable slot
int firstEmptyIndex = -1;
for (int i = 0; i < textures.size(); i++) {
const SharedObjectPointer& texture = textures.at(i);
if (texture) {
if (texture->equals(_edit.texture.data())) {
textureIndex = i + 1;
break;
HeightfieldColorDataPointer colorPointer = info.inputValues.at(0).getInlineValue<HeightfieldColorDataPointer>();
if (colorPointer) {
QByteArray contents(colorPointer->getContents());
int size = glm::sqrt((float)contents.size() / DataBlock::COLOR_BYTES);
int highest = size - 1;
float heightScale = size / info.size;
glm::vec3 center = (_position - info.minimum) * heightScale;
float scaledRadius = _radius * heightScale;
glm::vec3 extents(scaledRadius, scaledRadius, scaledRadius);
glm::vec3 start = glm::floor(center - extents);
glm::vec3 end = glm::ceil(center + extents);
// paint all points within the radius
float z = qMax(start.z, 0.0f);
float startX = qMax(start.x, 0.0f), endX = qMin(end.x, (float)highest);
int stride = size * DataBlock::COLOR_BYTES;
char* lineDest = contents.data() + (int)z * stride + (int)startX * DataBlock::COLOR_BYTES;
float squaredRadius = scaledRadius * scaledRadius;
char red = _color.red(), green = _color.green(), blue = _color.blue();
bool changed = false;
for (float endZ = qMin(end.z, (float)highest); z <= endZ; z += 1.0f) {
char* dest = lineDest;
for (float x = startX; x <= endX; x += 1.0f, dest += DataBlock::COLOR_BYTES) {
float dx = x - center.x, dz = z - center.z;
if (dx * dx + dz * dz <= squaredRadius) {
dest[0] = red;
dest[1] = green;
dest[2] = blue;
changed = true;
}
} else if (firstEmptyIndex == -1) {
firstEmptyIndex = i;
}
lineDest += stride;
}
// if nothing found, use the first empty slot or append
if (textureIndex == 0) {
if (firstEmptyIndex != -1) {
textures[firstEmptyIndex] = _edit.texture;
textureIndex = firstEmptyIndex + 1;
} else if (textures.size() < EIGHT_BIT_MAXIMUM) {
textures.append(_edit.texture);
textureIndex = textures.size();
} else {
// last resort: find the least-used texture and remove it
QHash<uchar, int> counts = countIndices(contents);
int lowestCount = INT_MAX;
for (QHash<uchar, int>::const_iterator it = counts.constBegin(); it != counts.constEnd(); it++) {
if (it.value() < lowestCount) {
textureIndex = it.key();
lowestCount = it.value();
}
if (changed) {
HeightfieldColorDataPointer newPointer(new HeightfieldColorData(contents));
info.outputValues[0] = AttributeValue(info.inputValues.at(0).getAttribute(),
encodeInline<HeightfieldColorDataPointer>(newPointer));
}
}
HeightfieldMaterialDataPointer materialPointer = info.inputValues.at(1).getInlineValue<HeightfieldMaterialDataPointer>();
if (materialPointer) {
QVector<SharedObjectPointer> materials = materialPointer->getMaterials();
QByteArray contents(materialPointer->getContents());
uchar materialIndex = getMaterialIndex(_material, materials, contents);
int size = glm::sqrt((float)contents.size());
int highest = size - 1;
float heightScale = highest / info.size;
glm::vec3 center = (_position - info.minimum) * heightScale;
float scaledRadius = _radius * heightScale;
glm::vec3 extents(scaledRadius, scaledRadius, scaledRadius);
glm::vec3 start = glm::floor(center - extents);
glm::vec3 end = glm::ceil(center + extents);
// paint all points within the radius
float z = qMax(start.z, 0.0f);
float startX = qMax(start.x, 0.0f), endX = qMin(end.x, (float)highest);
uchar* lineDest = (uchar*)contents.data() + (int)z * size + (int)startX;
float squaredRadius = scaledRadius * scaledRadius;
bool changed = false;
QHash<uchar, int> counts;
for (float endZ = qMin(end.z, (float)highest); z <= endZ; z += 1.0f) {
uchar* dest = lineDest;
for (float x = startX; x <= endX; x += 1.0f, dest++) {
float dx = x - center.x, dz = z - center.z;
if (dx * dx + dz * dz <= squaredRadius) {
*dest = materialIndex;
changed = true;
}
contents.replace((char)textureIndex, (char)0);
}
lineDest += size;
}
if (changed) {
clearUnusedMaterials(materials, contents);
HeightfieldMaterialDataPointer newPointer(new HeightfieldMaterialData(contents, materials));
info.outputValues[1] = AttributeValue(_outputs.at(1), encodeInline<HeightfieldMaterialDataPointer>(newPointer));
}
}
int size = glm::sqrt((float)contents.size());
int highest = size - 1;
float heightScale = highest / info.size;
glm::vec3 center = (_edit.position - info.minimum) * heightScale;
float scaledRadius = _edit.radius * heightScale;
glm::vec3 extents(scaledRadius, scaledRadius, scaledRadius);
glm::vec3 start = glm::floor(center - extents);
glm::vec3 end = glm::ceil(center + extents);
// paint all points within the radius
float z = qMax(start.z, 0.0f);
float startX = qMax(start.x, 0.0f), endX = qMin(end.x, (float)highest);
uchar* lineDest = (uchar*)contents.data() + (int)z * size + (int)startX;
float squaredRadius = scaledRadius * scaledRadius;
bool changed = false;
QHash<uchar, int> counts;
for (float endZ = qMin(end.z, (float)highest); z <= endZ; z += 1.0f) {
uchar* dest = lineDest;
for (float x = startX; x <= endX; x += 1.0f, dest++) {
float dx = x - center.x, dz = z - center.z;
if (dx * dx + dz * dz <= squaredRadius) {
*dest = textureIndex;
changed = true;
}
}
lineDest += size;
}
if (changed) {
// clear any unused textures
QHash<uchar, int> counts = countIndices(contents);
for (int i = 0; i < textures.size(); i++) {
if (counts.value(i + 1) == 0) {
textures[i] = SharedObjectPointer();
}
}
while (!(textures.isEmpty() || textures.last())) {
textures.removeLast();
}
HeightfieldTextureDataPointer newPointer(new HeightfieldTextureData(contents, textures));
info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline<HeightfieldTextureDataPointer>(newPointer));
}
paintColor(info, 1, _edit.position, _edit.radius, _edit.averageColor);
return STOP_RECURSION;
}
void PaintHeightfieldTextureEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const {
PaintHeightfieldTextureEditVisitor visitor(*this);
void PaintHeightfieldColorEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const {
PaintHeightfieldMaterialEditVisitor visitor(position, radius, SharedObjectPointer(), color);
data.guide(visitor);
}
PaintHeightfieldMaterialEdit::PaintHeightfieldMaterialEdit(const glm::vec3& position, float radius,
const SharedObjectPointer& material, const QColor& averageColor) :
position(position),
radius(radius),
material(material),
averageColor(averageColor) {
}
void PaintHeightfieldMaterialEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const {
PaintHeightfieldMaterialEditVisitor visitor(position, radius, material, averageColor);
data.guide(visitor);
}
VoxelColorBoxEdit::VoxelColorBoxEdit(const Box& region, float granularity, const QColor& color) :
region(region),
granularity(granularity),
color(color) {
}
const int VOXEL_BLOCK_SIZE = 16;
const int VOXEL_BLOCK_SAMPLES = VOXEL_BLOCK_SIZE + 1;
const int VOXEL_BLOCK_AREA = VOXEL_BLOCK_SAMPLES * VOXEL_BLOCK_SAMPLES;
const int VOXEL_BLOCK_VOLUME = VOXEL_BLOCK_AREA * VOXEL_BLOCK_SAMPLES;
class VoxelMaterialBoxEditVisitor : public MetavoxelVisitor {
public:
VoxelMaterialBoxEditVisitor(const Box& region, float granularity,
const SharedObjectPointer& material, const QColor& color);
virtual int visit(MetavoxelInfo& info);
private:
Box _region;
SharedObjectPointer _material;
QColor _color;
float _blockSize;
};
VoxelMaterialBoxEditVisitor::VoxelMaterialBoxEditVisitor(const Box& region, float granularity,
const SharedObjectPointer& material, const QColor& color) :
MetavoxelVisitor(QVector<AttributePointer>() << AttributeRegistry::getInstance()->getVoxelColorAttribute() <<
AttributeRegistry::getInstance()->getVoxelHermiteAttribute() <<
AttributeRegistry::getInstance()->getVoxelMaterialAttribute(), QVector<AttributePointer>() <<
AttributeRegistry::getInstance()->getVoxelColorAttribute() <<
AttributeRegistry::getInstance()->getVoxelHermiteAttribute() <<
AttributeRegistry::getInstance()->getVoxelMaterialAttribute()),
_region(region),
_material(material),
_color(color),
_blockSize(granularity * VOXEL_BLOCK_SIZE) {
}
int VoxelMaterialBoxEditVisitor::visit(MetavoxelInfo& info) {
Box bounds = info.getBounds();
if (!bounds.intersects(_region)) {
return STOP_RECURSION;
}
if (info.size > _blockSize) {
return DEFAULT_ORDER;
}
VoxelColorDataPointer colorPointer = info.inputValues.at(0).getInlineValue<VoxelColorDataPointer>();
QVector<QRgb> colorContents = (colorPointer && colorPointer->getSize() == VOXEL_BLOCK_SAMPLES) ?
colorPointer->getContents() : QVector<QRgb>(VOXEL_BLOCK_VOLUME);
Box overlap = info.getBounds().getIntersection(_region);
float scale = VOXEL_BLOCK_SIZE / 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();
for (QRgb* destZ = colorContents.data() + minZ * VOXEL_BLOCK_AREA + minY * VOXEL_BLOCK_SAMPLES + minX,
*endZ = destZ + sizeZ * VOXEL_BLOCK_AREA; destZ != endZ; destZ += VOXEL_BLOCK_AREA) {
for (QRgb* destY = destZ, *endY = destY + sizeY * VOXEL_BLOCK_SAMPLES; destY != endY; destY += VOXEL_BLOCK_SAMPLES) {
for (QRgb* destX = destY, *endX = destX + sizeX; destX != endX; destX++) {
*destX = rgb;
}
}
}
VoxelColorDataPointer newColorPointer(new VoxelColorData(colorContents, VOXEL_BLOCK_SAMPLES));
info.outputValues[0] = AttributeValue(info.inputValues.at(0).getAttribute(),
encodeInline<VoxelColorDataPointer>(newColorPointer));
VoxelHermiteDataPointer hermitePointer = info.inputValues.at(1).getInlineValue<VoxelHermiteDataPointer>();
QVector<QRgb> hermiteContents = (hermitePointer && hermitePointer->getSize() == VOXEL_BLOCK_SAMPLES) ?
hermitePointer->getContents() : QVector<QRgb>(VOXEL_BLOCK_VOLUME * VoxelHermiteData::EDGE_COUNT);
int hermiteArea = VOXEL_BLOCK_AREA * VoxelHermiteData::EDGE_COUNT;
int hermiteSamples = VOXEL_BLOCK_SAMPLES * VoxelHermiteData::EDGE_COUNT;
int hermiteMinX = minX, hermiteMinY = minY, hermiteMinZ = minZ;
int hermiteSizeX = sizeX, hermiteSizeY = sizeY, hermiteSizeZ = sizeZ;
if (minX > 0) {
hermiteMinX--;
hermiteSizeX++;
}
if (minY > 0) {
hermiteMinY--;
hermiteSizeY++;
}
if (minZ > 0) {
hermiteMinZ--;
hermiteSizeZ++;
}
const int NORMAL_MAX = 127;
QRgb* hermiteDestZ = hermiteContents.data() + hermiteMinZ * hermiteArea + hermiteMinY * hermiteSamples +
hermiteMinX * VoxelHermiteData::EDGE_COUNT;
for (int z = hermiteMinZ, hermiteMaxZ = z + hermiteSizeZ - 1; z <= hermiteMaxZ; z++, hermiteDestZ += hermiteArea) {
QRgb* hermiteDestY = hermiteDestZ;
for (int y = hermiteMinY, hermiteMaxY = y + hermiteSizeY - 1; y <= hermiteMaxY; y++, hermiteDestY += hermiteSamples) {
QRgb* hermiteDestX = hermiteDestY;
for (int x = hermiteMinX, hermiteMaxX = x + hermiteSizeX - 1; x <= hermiteMaxX; x++,
hermiteDestX += VoxelHermiteData::EDGE_COUNT) {
// internal edges are set to zero; border edges (when non-terminal) are set to the intersection values
hermiteDestX[0] = 0x0;
if ((x == hermiteMinX || x == hermiteMaxX) && x != VOXEL_BLOCK_SIZE) {
const QRgb* color = colorContents.constData() + z * VOXEL_BLOCK_AREA + y * VOXEL_BLOCK_SAMPLES + x;
int alpha0 = qAlpha(color[0]);
if (alpha0 != qAlpha(color[1])) {
hermiteDestX[0] = qRgba(alpha0 == 0 ? -NORMAL_MAX : NORMAL_MAX, 0, 0,
((x == hermiteMinX ? overlap.minimum.x : overlap.maximum.x) - x) * EIGHT_BIT_MAXIMUM);
}
}
hermiteDestX[1] = 0x0;
if ((y == hermiteMinY || y == hermiteMaxY) && y != VOXEL_BLOCK_SIZE) {
const QRgb* color = colorContents.constData() + z * VOXEL_BLOCK_AREA + y * VOXEL_BLOCK_SAMPLES + x;
int alpha0 = qAlpha(color[0]);
if (alpha0 != qAlpha(color[VOXEL_BLOCK_SAMPLES])) {
hermiteDestX[1] = qRgba(0, alpha0 == 0 ? -NORMAL_MAX : NORMAL_MAX, 0,
((y == hermiteMinY ? overlap.minimum.y : overlap.maximum.y) - y) * EIGHT_BIT_MAXIMUM);
}
}
hermiteDestX[2] = 0x0;
if ((z == hermiteMinZ || z == hermiteMaxZ) && z != VOXEL_BLOCK_SIZE) {
const QRgb* color = colorContents.constData() + z * VOXEL_BLOCK_AREA + y * VOXEL_BLOCK_SAMPLES + x;
int alpha0 = qAlpha(color[0]);
if (alpha0 != qAlpha(color[VOXEL_BLOCK_AREA])) {
hermiteDestX[2] = qRgba(0, 0, alpha0 == 0 ? -NORMAL_MAX : NORMAL_MAX,
((z == hermiteMinZ ? overlap.minimum.z : overlap.maximum.z) - z) * EIGHT_BIT_MAXIMUM);
}
}
}
}
}
VoxelHermiteDataPointer newHermitePointer(new VoxelHermiteData(hermiteContents, VOXEL_BLOCK_SAMPLES));
info.outputValues[1] = AttributeValue(info.inputValues.at(1).getAttribute(),
encodeInline<VoxelHermiteDataPointer>(newHermitePointer));
VoxelMaterialDataPointer materialPointer = info.inputValues.at(2).getInlineValue<VoxelMaterialDataPointer>();
QByteArray materialContents;
QVector<SharedObjectPointer> materials;
if (materialPointer && materialPointer->getSize() == VOXEL_BLOCK_SAMPLES) {
materialContents = materialPointer->getContents();
materials = materialPointer->getMaterials();
} else {
materialContents = QByteArray(VOXEL_BLOCK_VOLUME, 0);
}
uchar materialIndex = getMaterialIndex(_material, materials, materialContents);
for (uchar* destZ = (uchar*)materialContents.data() + minZ * VOXEL_BLOCK_AREA + minY * VOXEL_BLOCK_SAMPLES + minX,
*endZ = destZ + sizeZ * VOXEL_BLOCK_AREA; destZ != endZ; destZ += VOXEL_BLOCK_AREA) {
for (uchar* destY = destZ, *endY = destY + sizeY * VOXEL_BLOCK_SAMPLES; destY != endY; destY += VOXEL_BLOCK_SAMPLES) {
for (uchar* destX = destY, *endX = destX + sizeX; destX != endX; destX++) {
*destX = materialIndex;
}
}
}
clearUnusedMaterials(materials, materialContents);
VoxelMaterialDataPointer newMaterialPointer(new VoxelMaterialData(materialContents, VOXEL_BLOCK_SAMPLES, materials));
info.outputValues[2] = AttributeValue(_inputs.at(2), encodeInline<VoxelMaterialDataPointer>(newMaterialPointer));
return STOP_RECURSION;
}
void VoxelColorBoxEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const {
// expand to fit the entire edit
while (!data.getBounds().contains(region)) {
data.expand();
}
VoxelMaterialBoxEditVisitor visitor(region, granularity, SharedObjectPointer(), color);
data.guide(visitor);
}
VoxelMaterialBoxEdit::VoxelMaterialBoxEdit(const Box& region, float granularity,
const SharedObjectPointer& material, const QColor& averageColor) :
region(region),
granularity(granularity),
material(material),
averageColor(averageColor) {
}
void VoxelMaterialBoxEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const {
// expand to fit the entire edit
while (!data.getBounds().contains(region)) {
data.expand();
}
VoxelMaterialBoxEditVisitor visitor(region, granularity, material, averageColor);
data.guide(visitor);
}
VoxelColorSphereEdit::VoxelColorSphereEdit(const glm::vec3& center, float radius, float granularity, const QColor& color) :
center(center),
radius(radius),
granularity(granularity),
color(color) {
}
class VoxelMaterialSphereEditVisitor : public MetavoxelVisitor {
public:
VoxelMaterialSphereEditVisitor(const glm::vec3& center, float radius, const Box& bounds, float granularity,
const SharedObjectPointer& material, const QColor& color);
virtual int visit(MetavoxelInfo& info);
private:
glm::vec3 _center;
float _radius;
Box _bounds;
SharedObjectPointer _material;
QColor _color;
float _blockSize;
};
VoxelMaterialSphereEditVisitor::VoxelMaterialSphereEditVisitor(const glm::vec3& center, float radius, const Box& bounds,
float granularity, const SharedObjectPointer& material, const QColor& color) :
MetavoxelVisitor(QVector<AttributePointer>() << AttributeRegistry::getInstance()->getVoxelColorAttribute() <<
AttributeRegistry::getInstance()->getVoxelHermiteAttribute() <<
AttributeRegistry::getInstance()->getVoxelMaterialAttribute(), QVector<AttributePointer>() <<
AttributeRegistry::getInstance()->getVoxelColorAttribute() <<
AttributeRegistry::getInstance()->getVoxelHermiteAttribute() <<
AttributeRegistry::getInstance()->getVoxelMaterialAttribute()),
_center(center),
_radius(radius),
_bounds(bounds),
_material(material),
_color(color),
_blockSize(granularity * VOXEL_BLOCK_SIZE) {
}
int VoxelMaterialSphereEditVisitor::visit(MetavoxelInfo& info) {
Box bounds = info.getBounds();
if (!bounds.intersects(_bounds)) {
return STOP_RECURSION;
}
if (info.size > _blockSize) {
return DEFAULT_ORDER;
}
VoxelColorDataPointer colorPointer = info.inputValues.at(0).getInlineValue<VoxelColorDataPointer>();
QVector<QRgb> colorContents = (colorPointer && colorPointer->getSize() == VOXEL_BLOCK_SAMPLES) ?
colorPointer->getContents() : QVector<QRgb>(VOXEL_BLOCK_VOLUME);
Box overlap = info.getBounds().getIntersection(_bounds);
float scale = VOXEL_BLOCK_SIZE / 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;
glm::vec3 relativeCenter = (_center - info.minimum) * scale;
float relativeRadius = _radius * scale;
float relativeRadiusSquared = relativeRadius * relativeRadius;
QRgb rgb = _color.rgba();
glm::vec3 position(0.0f, 0.0f, minZ);
for (QRgb* destZ = colorContents.data() + minZ * VOXEL_BLOCK_AREA + minY * VOXEL_BLOCK_SAMPLES + minX,
*endZ = destZ + sizeZ * VOXEL_BLOCK_AREA; destZ != endZ; destZ += VOXEL_BLOCK_AREA, position.z++) {
position.y = minY;
for (QRgb* destY = destZ, *endY = destY + sizeY * VOXEL_BLOCK_SAMPLES; destY != endY;
destY += VOXEL_BLOCK_SAMPLES, position.y++) {
position.x = minX;
for (QRgb* destX = destY, *endX = destX + sizeX; destX != endX; destX++, position.x++) {
if (glm::distance(relativeCenter, position) <= relativeRadius) {
*destX = rgb;
}
}
}
}
VoxelColorDataPointer newColorPointer(new VoxelColorData(colorContents, VOXEL_BLOCK_SAMPLES));
info.outputValues[0] = AttributeValue(info.inputValues.at(0).getAttribute(),
encodeInline<VoxelColorDataPointer>(newColorPointer));
VoxelHermiteDataPointer hermitePointer = info.inputValues.at(1).getInlineValue<VoxelHermiteDataPointer>();
QVector<QRgb> hermiteContents = (hermitePointer && hermitePointer->getSize() == VOXEL_BLOCK_SAMPLES) ?
hermitePointer->getContents() : QVector<QRgb>(VOXEL_BLOCK_VOLUME * VoxelHermiteData::EDGE_COUNT);
int hermiteArea = VOXEL_BLOCK_AREA * VoxelHermiteData::EDGE_COUNT;
int hermiteSamples = VOXEL_BLOCK_SAMPLES * VoxelHermiteData::EDGE_COUNT;
int hermiteMinX = minX, hermiteMinY = minY, hermiteMinZ = minZ;
int hermiteSizeX = sizeX, hermiteSizeY = sizeY, hermiteSizeZ = sizeZ;
if (minX > 0) {
hermiteMinX--;
hermiteSizeX++;
}
if (minY > 0) {
hermiteMinY--;
hermiteSizeY++;
}
if (minZ > 0) {
hermiteMinZ--;
hermiteSizeZ++;
}
QRgb* hermiteDestZ = hermiteContents.data() + hermiteMinZ * hermiteArea + hermiteMinY * hermiteSamples +
hermiteMinX * VoxelHermiteData::EDGE_COUNT;
for (int z = hermiteMinZ, hermiteMaxZ = z + hermiteSizeZ - 1; z <= hermiteMaxZ; z++, hermiteDestZ += hermiteArea) {
QRgb* hermiteDestY = hermiteDestZ;
for (int y = hermiteMinY, hermiteMaxY = y + hermiteSizeY - 1; y <= hermiteMaxY; y++, hermiteDestY += hermiteSamples) {
QRgb* hermiteDestX = hermiteDestY;
for (int x = hermiteMinX, hermiteMaxX = x + hermiteSizeX - 1; x <= hermiteMaxX; x++,
hermiteDestX += VoxelHermiteData::EDGE_COUNT) {
// at each intersected non-terminal edge, we check for a transition and, if one is detected, we assign the
// crossing and normal values based on intersection with the sphere
hermiteDestX[0] = 0x0;
glm::vec3 offset(x - relativeCenter.x, y - relativeCenter.y, z - relativeCenter.z);
if (x != VOXEL_BLOCK_SIZE) {
const QRgb* color = colorContents.constData() + z * VOXEL_BLOCK_AREA + y * VOXEL_BLOCK_SAMPLES + x;
int alpha0 = qAlpha(color[0]);
if (alpha0 != qAlpha(color[1])) {
float radicand = relativeRadiusSquared - offset.y * offset.y - offset.z * offset.z;
float parameter = 0.5f;
if (radicand >= 0.0f) {
float root = glm::sqrt(radicand);
parameter = -offset.x - root;
if (parameter < 0.0f || parameter > 1.0f) {
parameter = glm::clamp(-offset.x + root, 0.0f, 1.0f);
}
}
glm::vec3 normal = offset + glm::vec3(parameter, 0.0f, 0.0f);
float length = glm::length(normal);
if (length > EPSILON) {
normal /= length;
} else {
normal = glm::vec3(0.0f, 1.0f, 0.0f);
}
hermiteDestX[0] = packNormal(normal, parameter * EIGHT_BIT_MAXIMUM);
}
}
hermiteDestX[1] = 0x0;
if (y != VOXEL_BLOCK_SIZE) {
const QRgb* color = colorContents.constData() + z * VOXEL_BLOCK_AREA + y * VOXEL_BLOCK_SAMPLES + x;
int alpha0 = qAlpha(color[0]);
if (alpha0 != qAlpha(color[VOXEL_BLOCK_SAMPLES])) {
float radicand = relativeRadiusSquared - offset.x * offset.x - offset.z * offset.z;
float parameter = 0.5f;
if (radicand >= 0.0f) {
float root = glm::sqrt(radicand);
parameter = -offset.y - root;
if (parameter < 0.0f || parameter > 1.0f) {
parameter = glm::clamp(-offset.y + root, 0.0f, 1.0f);
}
}
glm::vec3 normal = offset + glm::vec3(parameter, 0.0f, 0.0f);
float length = glm::length(normal);
if (length > EPSILON) {
normal /= length;
} else {
normal = glm::vec3(1.0f, 0.0f, 0.0f);
}
hermiteDestX[1] = packNormal(normal, parameter * EIGHT_BIT_MAXIMUM);
}
}
hermiteDestX[2] = 0x0;
if (z != VOXEL_BLOCK_SIZE) {
const QRgb* color = colorContents.constData() + z * VOXEL_BLOCK_AREA + y * VOXEL_BLOCK_SAMPLES + x;
int alpha0 = qAlpha(color[0]);
if (alpha0 != qAlpha(color[VOXEL_BLOCK_AREA])) {
float radicand = relativeRadiusSquared - offset.x * offset.x - offset.y * offset.y;
float parameter = 0.5f;
if (radicand >= 0.0f) {
float root = glm::sqrt(radicand);
parameter = -offset.z - root;
if (parameter < 0.0f || parameter > 1.0f) {
parameter = glm::clamp(-offset.z + root, 0.0f, 1.0f);
}
}
glm::vec3 normal = offset + glm::vec3(parameter, 0.0f, 0.0f);
float length = glm::length(normal);
if (length > EPSILON) {
normal /= length;
} else {
normal = glm::vec3(1.0f, 0.0f, 0.0f);
}
hermiteDestX[2] = packNormal(normal, parameter * EIGHT_BIT_MAXIMUM);
}
}
}
}
}
VoxelHermiteDataPointer newHermitePointer(new VoxelHermiteData(hermiteContents, VOXEL_BLOCK_SAMPLES));
info.outputValues[1] = AttributeValue(info.inputValues.at(1).getAttribute(),
encodeInline<VoxelHermiteDataPointer>(newHermitePointer));
VoxelMaterialDataPointer materialPointer = info.inputValues.at(2).getInlineValue<VoxelMaterialDataPointer>();
QByteArray materialContents;
QVector<SharedObjectPointer> materials;
if (materialPointer && materialPointer->getSize() == VOXEL_BLOCK_SAMPLES) {
materialContents = materialPointer->getContents();
materials = materialPointer->getMaterials();
} else {
materialContents = QByteArray(VOXEL_BLOCK_VOLUME, 0);
}
uchar materialIndex = getMaterialIndex(_material, materials, materialContents);
position.z = minZ;
for (uchar* destZ = (uchar*)materialContents.data() + minZ * VOXEL_BLOCK_AREA + minY * VOXEL_BLOCK_SAMPLES + minX,
*endZ = destZ + sizeZ * VOXEL_BLOCK_AREA; destZ != endZ; destZ += VOXEL_BLOCK_AREA, position.z++) {
position.y = minY;
for (uchar* destY = destZ, *endY = destY + sizeY * VOXEL_BLOCK_SAMPLES; destY != endY;
destY += VOXEL_BLOCK_SAMPLES, position.y++) {
position.x = minX;
for (uchar* destX = destY, *endX = destX + sizeX; destX != endX; destX++, position.x++) {
if (glm::distance(relativeCenter, position) <= relativeRadius) {
*destX = materialIndex;
}
}
}
}
clearUnusedMaterials(materials, materialContents);
VoxelMaterialDataPointer newMaterialPointer(new VoxelMaterialData(materialContents, VOXEL_BLOCK_SAMPLES, materials));
info.outputValues[2] = AttributeValue(_inputs.at(2), encodeInline<VoxelMaterialDataPointer>(newMaterialPointer));
return STOP_RECURSION;
}
void VoxelColorSphereEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const {
// expand to fit the entire edit
glm::vec3 extents(radius, radius, radius);
Box bounds(center - extents, center + extents);
while (!data.getBounds().contains(bounds)) {
data.expand();
}
VoxelMaterialSphereEditVisitor visitor(center, radius, bounds, granularity, SharedObjectPointer(), color);
data.guide(visitor);
}
VoxelMaterialSphereEdit::VoxelMaterialSphereEdit(const glm::vec3& center, float radius, float granularity,
const SharedObjectPointer& material, const QColor& averageColor) :
center(center),
radius(radius),
granularity(granularity),
material(material),
averageColor(averageColor) {
}
void VoxelMaterialSphereEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const {
// expand to fit the entire edit
glm::vec3 extents(radius, radius, radius);
Box bounds(center - extents, center + extents);
while (!data.getBounds().contains(bounds)) {
data.expand();
}
VoxelMaterialSphereEditVisitor visitor(center, radius, bounds, granularity, material, averageColor);
data.guide(visitor);
}

View file

@ -241,23 +241,98 @@ public:
DECLARE_STREAMABLE_METATYPE(PaintHeightfieldColorEdit)
/// An edit that sets a region of a heightfield texture.
class PaintHeightfieldTextureEdit : public MetavoxelEdit {
/// An edit that sets a region of a heightfield material.
class PaintHeightfieldMaterialEdit : public MetavoxelEdit {
STREAMABLE
public:
STREAM glm::vec3 position;
STREAM float radius;
STREAM SharedObjectPointer texture;
STREAM SharedObjectPointer material;
STREAM QColor averageColor;
PaintHeightfieldTextureEdit(const glm::vec3& position = glm::vec3(), float radius = 0.0f,
const SharedObjectPointer& texture = SharedObjectPointer(), const QColor& averageColor = QColor());
PaintHeightfieldMaterialEdit(const glm::vec3& position = glm::vec3(), float radius = 0.0f,
const SharedObjectPointer& material = SharedObjectPointer(), const QColor& averageColor = QColor());
virtual void apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const;
};
DECLARE_STREAMABLE_METATYPE(PaintHeightfieldTextureEdit)
DECLARE_STREAMABLE_METATYPE(PaintHeightfieldMaterialEdit)
/// An edit that sets the color of voxels within a box to a value.
class VoxelColorBoxEdit : public MetavoxelEdit {
STREAMABLE
public:
STREAM Box region;
STREAM float granularity;
STREAM QColor color;
VoxelColorBoxEdit(const Box& region = Box(), float granularity = 0.0f, const QColor& color = QColor());
virtual void apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const;
};
DECLARE_STREAMABLE_METATYPE(VoxelColorBoxEdit)
/// An edit that sets the materials of voxels within a box to a value.
class VoxelMaterialBoxEdit : public MetavoxelEdit {
STREAMABLE
public:
STREAM Box region;
STREAM float granularity;
STREAM SharedObjectPointer material;
STREAM QColor averageColor;
VoxelMaterialBoxEdit(const Box& region = Box(), float granularity = 0.0f,
const SharedObjectPointer& material = SharedObjectPointer(), const QColor& averageColor = QColor());
virtual void apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const;
};
DECLARE_STREAMABLE_METATYPE(VoxelMaterialBoxEdit)
/// An edit that sets the color of voxels within a sphere to a value.
class VoxelColorSphereEdit : public MetavoxelEdit {
STREAMABLE
public:
STREAM glm::vec3 center;
STREAM float radius;
STREAM float granularity;
STREAM QColor color;
VoxelColorSphereEdit(const glm::vec3& center = glm::vec3(), float radius = 0.0f,
float granularity = 0.0f, const QColor& color = QColor());
virtual void apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const;
};
DECLARE_STREAMABLE_METATYPE(VoxelColorSphereEdit)
/// An edit that sets the materials of voxels within a sphere to a value.
class VoxelMaterialSphereEdit : public MetavoxelEdit {
STREAMABLE
public:
STREAM glm::vec3 center;
STREAM float radius;
STREAM float granularity;
STREAM SharedObjectPointer material;
STREAM QColor averageColor;
VoxelMaterialSphereEdit(const glm::vec3& center = glm::vec3(), float radius = 0.0f, float granularity = 0.0f,
const SharedObjectPointer& material = SharedObjectPointer(), const QColor& averageColor = QColor());
virtual void apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const;
};
DECLARE_STREAMABLE_METATYPE(VoxelMaterialSphereEdit)
#endif // hifi_MetavoxelMessages_h