mirror of
https://github.com/overte-org/overte.git
synced 2025-04-22 13:53:26 +02:00
Working on texture rendering.
This commit is contained in:
parent
7cf7f188f8
commit
416d9bac2e
14 changed files with 473 additions and 104 deletions
interface
resources/shaders
metavoxel_heightfield_base.fragmetavoxel_heightfield_base.vertmetavoxel_heightfield_light.fragmetavoxel_heightfield_light.vertmetavoxel_heightfield_light_cascaded_shadow_map.fragmetavoxel_heightfield_light_shadow_map.fragmetavoxel_heightfield_splat.fragmetavoxel_heightfield_splat.vert
src
libraries/metavoxels/src
20
interface/resources/shaders/metavoxel_heightfield_base.frag
Normal file
20
interface/resources/shaders/metavoxel_heightfield_base.frag
Normal file
|
@ -0,0 +1,20 @@
|
|||
#version 120
|
||||
|
||||
//
|
||||
// metavoxel_heightfield_base.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 diffuse texture
|
||||
uniform sampler2D diffuseMap;
|
||||
|
||||
void main(void) {
|
||||
// compute the base color based on OpenGL lighting model
|
||||
gl_FragColor = gl_Color * texture2D(diffuseMap, gl_TexCoord[0].st);
|
||||
}
|
33
interface/resources/shaders/metavoxel_heightfield_base.vert
Normal file
33
interface/resources/shaders/metavoxel_heightfield_base.vert
Normal file
|
@ -0,0 +1,33 @@
|
|||
#version 120
|
||||
|
||||
//
|
||||
// metavoxel_heightfield_base.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 scale between height and color textures
|
||||
uniform float colorScale;
|
||||
|
||||
void main(void) {
|
||||
// add the height to the position
|
||||
float height = texture2D(heightMap, gl_MultiTexCoord0.st).r;
|
||||
gl_Position = gl_ModelViewProjectionMatrix * (gl_Vertex + vec4(0.0, height, 0.0, 0.0));
|
||||
|
||||
// the zero height should be invisible
|
||||
gl_FrontColor = vec4(1.0, 1.0, 1.0, step(height, 0.0));
|
||||
|
||||
// pass along the scaled/offset texture coordinates
|
||||
gl_TexCoord[0] = (gl_MultiTexCoord0 - vec4(heightScale, heightScale, 0.0, 0.0)) * colorScale;
|
||||
}
|
21
interface/resources/shaders/metavoxel_heightfield_light.frag
Normal file
21
interface/resources/shaders/metavoxel_heightfield_light.frag
Normal file
|
@ -0,0 +1,21 @@
|
|||
#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)));
|
||||
}
|
45
interface/resources/shaders/metavoxel_heightfield_light.vert
Normal file
45
interface/resources/shaders/metavoxel_heightfield_light.vert
Normal file
|
@ -0,0 +1,45 @@
|
|||
#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);
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
#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));
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
#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));
|
||||
}
|
20
interface/resources/shaders/metavoxel_heightfield_splat.frag
Normal file
20
interface/resources/shaders/metavoxel_heightfield_splat.frag
Normal file
|
@ -0,0 +1,20 @@
|
|||
#version 120
|
||||
|
||||
//
|
||||
// metavoxel_heightfield_splat.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 diffuse texture
|
||||
uniform sampler2D diffuseMap;
|
||||
|
||||
void main(void) {
|
||||
// compute the base color based on OpenGL lighting model
|
||||
gl_FragColor = gl_Color * texture2D(diffuseMap, gl_TexCoord[0].st);
|
||||
}
|
33
interface/resources/shaders/metavoxel_heightfield_splat.vert
Normal file
33
interface/resources/shaders/metavoxel_heightfield_splat.vert
Normal file
|
@ -0,0 +1,33 @@
|
|||
#version 120
|
||||
|
||||
//
|
||||
// metavoxel_heighfield_splat.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 scale between height and texture textures
|
||||
uniform float textureScale;
|
||||
|
||||
void main(void) {
|
||||
// add the height to the position
|
||||
float height = texture2D(heightMap, gl_MultiTexCoord0.st).r;
|
||||
gl_Position = gl_ModelViewProjectionMatrix * (gl_Vertex + vec4(0.0, height, 0.0, 0.0));
|
||||
|
||||
// the zero height should be invisible
|
||||
gl_FrontColor = vec4(1.0, 1.0, 1.0, step(height, 0.0));
|
||||
|
||||
// pass along the scaled/offset texture coordinates
|
||||
gl_TexCoord[0] = (gl_MultiTexCoord0 - vec4(heightScale, heightScale, 0.0, 0.0)) * textureScale;
|
||||
}
|
|
@ -602,24 +602,23 @@ const int HeightfieldBuffer::SHARED_EDGE = 1;
|
|||
const int HeightfieldBuffer::HEIGHT_EXTENSION = 2 * HeightfieldBuffer::HEIGHT_BORDER + HeightfieldBuffer::SHARED_EDGE;
|
||||
|
||||
HeightfieldBuffer::HeightfieldBuffer(const glm::vec3& translation, float scale,
|
||||
const QByteArray& height, const QByteArray& color, const QByteArray& texture) :
|
||||
const QByteArray& height, const QByteArray& color, const QByteArray& texture,
|
||||
const QVector<SharedObjectPointer>& textures) :
|
||||
_translation(translation),
|
||||
_scale(scale),
|
||||
_heightBounds(translation, translation + glm::vec3(scale, scale, scale)),
|
||||
_colorBounds(_heightBounds),
|
||||
_textureBounds(_heightBounds),
|
||||
_height(height),
|
||||
_color(color),
|
||||
_texture(texture),
|
||||
_textures(textures),
|
||||
_heightTextureID(0),
|
||||
_colorTextureID(0),
|
||||
_textureTextureID(0),
|
||||
_heightSize(glm::sqrt(height.size())),
|
||||
_heightIncrement(scale / (_heightSize - HEIGHT_EXTENSION)),
|
||||
_colorSize(glm::sqrt(color.size() / HeightfieldData::COLOR_BYTES)),
|
||||
_colorIncrement(scale / (_colorSize - SHARED_EDGE)),
|
||||
_textureSize(glm::sqrt(texture.size())),
|
||||
_textureIncrement(scale / (_textureSize - SHARED_EDGE)) {
|
||||
_colorIncrement(scale / (_colorSize - SHARED_EDGE)) {
|
||||
|
||||
_heightBounds.minimum.x -= _heightIncrement * HEIGHT_BORDER;
|
||||
_heightBounds.minimum.z -= _heightIncrement * HEIGHT_BORDER;
|
||||
|
@ -628,9 +627,6 @@ HeightfieldBuffer::HeightfieldBuffer(const glm::vec3& translation, float scale,
|
|||
|
||||
_colorBounds.maximum.x += _colorIncrement * SHARED_EDGE;
|
||||
_colorBounds.maximum.z += _colorIncrement * SHARED_EDGE;
|
||||
|
||||
_textureBounds.maximum.x += _textureIncrement * SHARED_EDGE;
|
||||
_textureBounds.maximum.z += _textureIncrement * SHARED_EDGE;
|
||||
}
|
||||
|
||||
HeightfieldBuffer::~HeightfieldBuffer() {
|
||||
|
@ -712,6 +708,15 @@ void HeightfieldBuffer::render(bool cursor) {
|
|||
int textureSize = glm::sqrt(_texture.size());
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, textureSize, textureSize, 0,
|
||||
GL_LUMINANCE, GL_UNSIGNED_BYTE, _texture.constData());
|
||||
|
||||
_networkTextures.resize(_textures.size());
|
||||
for (int i = 0; i < _textures.size(); i++) {
|
||||
const SharedObjectPointer texture = _textures.at(i);
|
||||
if (texture) {
|
||||
_networkTextures[i] = Application::getInstance()->getTextureCache()->getTexture(
|
||||
static_cast<HeightfieldTexture*>(texture.data())->getURL());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// create the buffer objects lazily
|
||||
|
@ -780,7 +785,56 @@ void HeightfieldBuffer::render(bool cursor) {
|
|||
|
||||
glBindTexture(GL_TEXTURE_2D, _heightTextureID);
|
||||
|
||||
if (!cursor) {
|
||||
if (cursor) {
|
||||
glDrawRangeElements(GL_TRIANGLES, 0, vertexCount - 1, indexCount, GL_UNSIGNED_INT, 0);
|
||||
|
||||
} else if (!_textures.isEmpty()) {
|
||||
DefaultMetavoxelRendererImplementation::getBaseHeightfieldProgram().bind();
|
||||
DefaultMetavoxelRendererImplementation::getBaseHeightfieldProgram().setUniformValue(
|
||||
DefaultMetavoxelRendererImplementation::getBaseHeightScaleLocation(), 1.0f / _heightSize);
|
||||
DefaultMetavoxelRendererImplementation::getBaseHeightfieldProgram().setUniformValue(
|
||||
DefaultMetavoxelRendererImplementation::getBaseColorScaleLocation(), (float)_heightSize / innerSize);
|
||||
glActiveTexture(GL_TEXTURE1);
|
||||
glBindTexture(GL_TEXTURE_2D, _colorTextureID);
|
||||
|
||||
glDrawRangeElements(GL_TRIANGLES, 0, vertexCount - 1, indexCount, GL_UNSIGNED_INT, 0);
|
||||
|
||||
glDepthFunc(GL_LEQUAL);
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_DST_COLOR, GL_ZERO);
|
||||
glEnable(GL_POLYGON_OFFSET_FILL);
|
||||
glPolygonOffset(-1.0f, -1.0f);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::SimpleShadows)) {
|
||||
DefaultMetavoxelRendererImplementation::getShadowLightHeightfieldProgram().bind();
|
||||
DefaultMetavoxelRendererImplementation::getShadowLightHeightfieldProgram().setUniformValue(
|
||||
DefaultMetavoxelRendererImplementation::getShadowLightHeightScaleLocation(), 1.0f / _heightSize);
|
||||
|
||||
} else if (Menu::getInstance()->isOptionChecked(MenuOption::CascadedShadows)) {
|
||||
DefaultMetavoxelRendererImplementation::getCascadedShadowLightHeightfieldProgram().bind();
|
||||
DefaultMetavoxelRendererImplementation::getCascadedShadowLightHeightfieldProgram().setUniformValue(
|
||||
DefaultMetavoxelRendererImplementation::getCascadedShadowLightHeightScaleLocation(), 1.0f / _heightSize);
|
||||
|
||||
} else {
|
||||
DefaultMetavoxelRendererImplementation::getLightHeightfieldProgram().bind();
|
||||
DefaultMetavoxelRendererImplementation::getLightHeightfieldProgram().setUniformValue(
|
||||
DefaultMetavoxelRendererImplementation::getBaseHeightScaleLocation(), 1.0f / _heightSize);
|
||||
}
|
||||
|
||||
glDrawRangeElements(GL_TRIANGLES, 0, vertexCount - 1, indexCount, GL_UNSIGNED_INT, 0);
|
||||
|
||||
DefaultMetavoxelRendererImplementation::getHeightfieldProgram().bind();
|
||||
|
||||
glDisable(GL_POLYGON_OFFSET_FILL);
|
||||
glDisable(GL_BLEND);
|
||||
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE);
|
||||
glDepthFunc(GL_LESS);
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
|
||||
} else {
|
||||
int heightScaleLocation = DefaultMetavoxelRendererImplementation::getHeightScaleLocation();
|
||||
int colorScaleLocation = DefaultMetavoxelRendererImplementation::getColorScaleLocation();
|
||||
ProgramObject* program = &DefaultMetavoxelRendererImplementation::getHeightfieldProgram();
|
||||
|
@ -798,11 +852,9 @@ void HeightfieldBuffer::render(bool cursor) {
|
|||
program->setUniformValue(colorScaleLocation, (float)_heightSize / innerSize);
|
||||
glActiveTexture(GL_TEXTURE1);
|
||||
glBindTexture(GL_TEXTURE_2D, _colorTextureID);
|
||||
}
|
||||
|
||||
glDrawRangeElements(GL_TRIANGLES, 0, vertexCount - 1, indexCount, GL_UNSIGNED_INT, 0);
|
||||
|
||||
if (!cursor) {
|
||||
|
||||
glDrawRangeElements(GL_TRIANGLES, 0, vertexCount - 1, indexCount, GL_UNSIGNED_INT, 0);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
}
|
||||
|
@ -919,6 +971,68 @@ void DefaultMetavoxelRendererImplementation::init() {
|
|||
_shadowDistancesLocation = _cascadedShadowMapHeightfieldProgram.uniformLocation("shadowDistances");
|
||||
_cascadedShadowMapHeightfieldProgram.release();
|
||||
|
||||
_baseHeightfieldProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() +
|
||||
"shaders/metavoxel_heightfield_base.vert");
|
||||
_baseHeightfieldProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() +
|
||||
"shaders/metavoxel_heightfield_base.frag");
|
||||
_baseHeightfieldProgram.link();
|
||||
|
||||
_baseHeightfieldProgram.bind();
|
||||
_baseHeightfieldProgram.setUniformValue("heightMap", 0);
|
||||
_baseHeightfieldProgram.setUniformValue("diffuseMap", 1);
|
||||
_baseHeightScaleLocation = _heightfieldProgram.uniformLocation("heightScale");
|
||||
_baseColorScaleLocation = _heightfieldProgram.uniformLocation("colorScale");
|
||||
_baseHeightfieldProgram.release();
|
||||
|
||||
_splatHeightfieldProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() +
|
||||
"shaders/metavoxel_heightfield_splat.vert");
|
||||
_splatHeightfieldProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() +
|
||||
"shaders/metavoxel_heightfield_splat.frag");
|
||||
_splatHeightfieldProgram.link();
|
||||
|
||||
_splatHeightfieldProgram.bind();
|
||||
_splatHeightfieldProgram.setUniformValue("heightMap", 0);
|
||||
_splatHeightfieldProgram.setUniformValue("diffuseMap", 1);
|
||||
_splatHeightScaleLocation = _splatHeightfieldProgram.uniformLocation("heightScale");
|
||||
_splatTextureScaleLocation = _splatHeightfieldProgram.uniformLocation("textureScale");
|
||||
_splatHeightfieldProgram.release();
|
||||
|
||||
_lightHeightfieldProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() +
|
||||
"shaders/metavoxel_heightfield_light.vert");
|
||||
_lightHeightfieldProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() +
|
||||
"shaders/metavoxel_heightfield_light.frag");
|
||||
_lightHeightfieldProgram.link();
|
||||
|
||||
_lightHeightfieldProgram.bind();
|
||||
_lightHeightfieldProgram.setUniformValue("heightMap", 0);
|
||||
_lightHeightScaleLocation = _lightHeightfieldProgram.uniformLocation("heightScale");
|
||||
_lightHeightfieldProgram.release();
|
||||
|
||||
_shadowLightHeightfieldProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() +
|
||||
"shaders/metavoxel_heightfield_light.vert");
|
||||
_shadowLightHeightfieldProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() +
|
||||
"shaders/metavoxel_heightfield_light_shadow_map.frag");
|
||||
_shadowLightHeightfieldProgram.link();
|
||||
|
||||
_shadowLightHeightfieldProgram.bind();
|
||||
_shadowLightHeightfieldProgram.setUniformValue("heightMap", 0);
|
||||
_shadowLightHeightfieldProgram.setUniformValue("shadowMap", 2);
|
||||
_shadowLightHeightScaleLocation = _shadowLightHeightfieldProgram.uniformLocation("heightScale");
|
||||
_shadowLightHeightfieldProgram.release();
|
||||
|
||||
_cascadedShadowLightHeightfieldProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() +
|
||||
"shaders/metavoxel_heightfield_light.vert");
|
||||
_cascadedShadowLightHeightfieldProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() +
|
||||
"shaders/metavoxel_heightfield_light_cascaded_shadow_map.frag");
|
||||
_cascadedShadowLightHeightfieldProgram.link();
|
||||
|
||||
_cascadedShadowLightHeightfieldProgram.bind();
|
||||
_cascadedShadowLightHeightfieldProgram.setUniformValue("heightMap", 0);
|
||||
_cascadedShadowLightHeightfieldProgram.setUniformValue("shadowMap", 2);
|
||||
_cascadedShadowLightHeightScaleLocation = _cascadedShadowLightHeightfieldProgram.uniformLocation("heightScale");
|
||||
_shadowLightDistancesLocation = _cascadedShadowLightHeightfieldProgram.uniformLocation("shadowDistances");
|
||||
_cascadedShadowLightHeightfieldProgram.release();
|
||||
|
||||
_heightfieldCursorProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() +
|
||||
"shaders/metavoxel_heightfield_cursor.vert");
|
||||
_heightfieldCursorProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() +
|
||||
|
@ -1019,8 +1133,7 @@ private:
|
|||
|
||||
HeightfieldFetchVisitor::HeightfieldFetchVisitor(const MetavoxelLOD& lod, const QVector<Box>& intersections) :
|
||||
MetavoxelVisitor(QVector<AttributePointer>() << AttributeRegistry::getInstance()->getHeightfieldAttribute() <<
|
||||
AttributeRegistry::getInstance()->getHeightfieldColorAttribute() <<
|
||||
AttributeRegistry::getInstance()->getHeightfieldTextureAttribute(), QVector<AttributePointer>(), lod),
|
||||
AttributeRegistry::getInstance()->getHeightfieldColorAttribute(), QVector<AttributePointer>(), lod),
|
||||
_intersections(intersections) {
|
||||
}
|
||||
|
||||
|
@ -1135,50 +1248,6 @@ int HeightfieldFetchVisitor::visit(MetavoxelInfo& info) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
int textureSize = _buffer->getTextureSize();
|
||||
if (textureSize == 0) {
|
||||
continue;
|
||||
}
|
||||
HeightfieldTextureDataPointer texture = info.inputValues.at(2).getInlineValue<HeightfieldTextureDataPointer>();
|
||||
if (!texture) {
|
||||
continue;
|
||||
}
|
||||
const Box& textureBounds = _buffer->getTextureBounds();
|
||||
overlap = textureBounds.getIntersection(overlap);
|
||||
float textureIncrement = _buffer->getTextureIncrement();
|
||||
destX = (overlap.minimum.x - textureBounds.minimum.x) / textureIncrement;
|
||||
destY = (overlap.minimum.z - textureBounds.minimum.z) / textureIncrement;
|
||||
destWidth = glm::ceil((overlap.maximum.x - overlap.minimum.x) / textureIncrement);
|
||||
destHeight = glm::ceil((overlap.maximum.z - overlap.minimum.z) / textureIncrement);
|
||||
dest = _buffer->getTexture().data() + destY * textureSize + destX;
|
||||
|
||||
const QByteArray& srcTexture = texture->getContents();
|
||||
srcSize = glm::sqrt(srcTexture.size());
|
||||
srcIncrement = info.size / srcSize;
|
||||
|
||||
if (srcIncrement == textureIncrement) {
|
||||
// easy case: same resolution
|
||||
int srcX = (overlap.minimum.x - info.minimum.x) / srcIncrement;
|
||||
int srcY = (overlap.minimum.z - info.minimum.z) / srcIncrement;
|
||||
|
||||
const char* src = srcTexture.constData() + srcY * srcSize + srcX;
|
||||
for (int y = 0; y < destHeight; y++, src += srcSize, dest += textureSize) {
|
||||
memcpy(dest, src, destWidth);
|
||||
}
|
||||
} else {
|
||||
// more difficult: different resolutions
|
||||
float srcX = (overlap.minimum.x - info.minimum.x) / srcIncrement;
|
||||
float srcY = (overlap.minimum.z - info.minimum.z) / srcIncrement;
|
||||
float srcAdvance = textureIncrement / srcIncrement;
|
||||
for (int y = 0; y < destHeight; y++, dest += textureSize, srcY += srcAdvance) {
|
||||
const char* src = srcTexture.constData() + (int)srcY * srcSize;
|
||||
float lineSrcX = srcX;
|
||||
for (char* lineDest = dest, *end = dest + destWidth; lineDest != end; lineDest++, lineSrcX += srcAdvance) {
|
||||
*lineDest = src[(int)lineSrcX];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return STOP_RECURSION;
|
||||
}
|
||||
|
@ -1233,42 +1302,43 @@ int HeightfieldRegionVisitor::visit(MetavoxelInfo& info) {
|
|||
}
|
||||
|
||||
HeightfieldTextureDataPointer texture = info.inputValues.at(2).getInlineValue<HeightfieldTextureDataPointer>();
|
||||
int textureContentsSize = 0;
|
||||
QByteArray textureContents;
|
||||
QVector<SharedObjectPointer> textures;
|
||||
if (texture) {
|
||||
const QByteArray& textureContents = texture->getContents();
|
||||
int textureSize = glm::sqrt(textureContents.size());
|
||||
int extendedTextureSize = textureSize + HeightfieldBuffer::SHARED_EDGE;
|
||||
textureContentsSize = extendedTextureSize * extendedTextureSize;
|
||||
textureContents = texture->getContents();
|
||||
textures = texture->getTextures();
|
||||
}
|
||||
|
||||
const HeightfieldBuffer* existingBuffer = static_cast<const HeightfieldBuffer*>(
|
||||
info.inputValues.at(3).getInlineValue<BufferDataPointer>().data());
|
||||
Box bounds = info.getBounds();
|
||||
if (existingBuffer && existingBuffer->getHeight().size() == heightContentsSize &&
|
||||
existingBuffer->getColor().size() == colorContentsSize &&
|
||||
existingBuffer->getTexture().size() == textureContentsSize) {
|
||||
existingBuffer->getColor().size() == colorContentsSize) {
|
||||
// we already have a buffer of the correct resolution
|
||||
addRegion(bounds, existingBuffer->getHeightBounds());
|
||||
return STOP_RECURSION;
|
||||
buffer = new HeightfieldBuffer(info.minimum, info.size, existingBuffer->getHeight(),
|
||||
existingBuffer->getColor(), textureContents, textures);
|
||||
|
||||
} else {
|
||||
// we must create a new buffer and update its borders
|
||||
buffer = new HeightfieldBuffer(info.minimum, info.size, QByteArray(heightContentsSize, 0),
|
||||
QByteArray(colorContentsSize, 0), textureContents, textures);
|
||||
const Box& heightBounds = buffer->getHeightBounds();
|
||||
addRegion(bounds, heightBounds);
|
||||
|
||||
_intersections.clear();
|
||||
_intersections.append(Box(heightBounds.minimum,
|
||||
glm::vec3(bounds.maximum.x, heightBounds.maximum.y, bounds.minimum.z)));
|
||||
_intersections.append(Box(glm::vec3(bounds.maximum.x, heightBounds.minimum.y, heightBounds.minimum.z),
|
||||
glm::vec3(heightBounds.maximum.x, heightBounds.maximum.y, bounds.maximum.z)));
|
||||
_intersections.append(Box(glm::vec3(bounds.minimum.x, heightBounds.minimum.y, bounds.maximum.z),
|
||||
heightBounds.maximum));
|
||||
_intersections.append(Box(glm::vec3(heightBounds.minimum.x, heightBounds.minimum.y, bounds.minimum.z),
|
||||
glm::vec3(bounds.minimum.x, heightBounds.maximum.y, heightBounds.maximum.z)));
|
||||
|
||||
_fetchVisitor.init(buffer);
|
||||
_data->guide(_fetchVisitor);
|
||||
}
|
||||
// we must create a new buffer and update its borders
|
||||
buffer = new HeightfieldBuffer(info.minimum, info.size, QByteArray(heightContentsSize, 0),
|
||||
QByteArray(colorContentsSize, 0), QByteArray(textureContentsSize, 0));
|
||||
const Box& heightBounds = buffer->getHeightBounds();
|
||||
addRegion(bounds, heightBounds);
|
||||
|
||||
_intersections.clear();
|
||||
_intersections.append(Box(heightBounds.minimum,
|
||||
glm::vec3(bounds.maximum.x, heightBounds.maximum.y, bounds.minimum.z)));
|
||||
_intersections.append(Box(glm::vec3(bounds.maximum.x, heightBounds.minimum.y, heightBounds.minimum.z),
|
||||
glm::vec3(heightBounds.maximum.x, heightBounds.maximum.y, bounds.maximum.z)));
|
||||
_intersections.append(Box(glm::vec3(bounds.minimum.x, heightBounds.minimum.y, bounds.maximum.z),
|
||||
heightBounds.maximum));
|
||||
_intersections.append(Box(glm::vec3(heightBounds.minimum.x, heightBounds.minimum.y, bounds.minimum.z),
|
||||
glm::vec3(bounds.minimum.x, heightBounds.maximum.y, heightBounds.maximum.z)));
|
||||
|
||||
_fetchVisitor.init(buffer);
|
||||
_data->guide(_fetchVisitor);
|
||||
}
|
||||
info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(BufferDataPointer(buffer)));
|
||||
return STOP_RECURSION;
|
||||
|
@ -1326,7 +1396,7 @@ int HeightfieldUpdateVisitor::visit(MetavoxelInfo& info) {
|
|||
return STOP_RECURSION;
|
||||
}
|
||||
HeightfieldBuffer* newBuffer = new HeightfieldBuffer(info.minimum, info.size,
|
||||
buffer->getHeight(), buffer->getColor(), buffer->getTexture());
|
||||
buffer->getHeight(), buffer->getColor(), buffer->getTexture(), buffer->getTextures());
|
||||
_fetchVisitor.init(newBuffer);
|
||||
_data->guide(_fetchVisitor);
|
||||
info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(BufferDataPointer(newBuffer)));
|
||||
|
@ -1514,6 +1584,9 @@ void DefaultMetavoxelRendererImplementation::render(MetavoxelData& data, Metavox
|
|||
ProgramObject* program = &_heightfieldProgram;
|
||||
if (Menu::getInstance()->getShadowsEnabled()) {
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::CascadedShadows)) {
|
||||
_cascadedShadowLightHeightfieldProgram.bind();
|
||||
_cascadedShadowLightHeightfieldProgram.setUniform(_shadowLightDistancesLocation,
|
||||
Application::getInstance()->getShadowDistances());
|
||||
program = &_cascadedShadowMapHeightfieldProgram;
|
||||
program->bind();
|
||||
program->setUniform(_shadowDistancesLocation, Application::getInstance()->getShadowDistances());
|
||||
|
@ -1559,6 +1632,19 @@ ProgramObject DefaultMetavoxelRendererImplementation::_cascadedShadowMapHeightfi
|
|||
int DefaultMetavoxelRendererImplementation::_cascadedShadowMapHeightScaleLocation;
|
||||
int DefaultMetavoxelRendererImplementation::_cascadedShadowMapColorScaleLocation;
|
||||
int DefaultMetavoxelRendererImplementation::_shadowDistancesLocation;
|
||||
ProgramObject DefaultMetavoxelRendererImplementation::_baseHeightfieldProgram;
|
||||
int DefaultMetavoxelRendererImplementation::_baseHeightScaleLocation;
|
||||
int DefaultMetavoxelRendererImplementation::_baseColorScaleLocation;
|
||||
ProgramObject DefaultMetavoxelRendererImplementation::_splatHeightfieldProgram;
|
||||
int DefaultMetavoxelRendererImplementation::_splatHeightScaleLocation;
|
||||
int DefaultMetavoxelRendererImplementation::_splatTextureScaleLocation;
|
||||
ProgramObject DefaultMetavoxelRendererImplementation::_lightHeightfieldProgram;
|
||||
int DefaultMetavoxelRendererImplementation::_lightHeightScaleLocation;
|
||||
ProgramObject DefaultMetavoxelRendererImplementation::_shadowLightHeightfieldProgram;
|
||||
int DefaultMetavoxelRendererImplementation::_shadowLightHeightScaleLocation;
|
||||
ProgramObject DefaultMetavoxelRendererImplementation::_cascadedShadowLightHeightfieldProgram;
|
||||
int DefaultMetavoxelRendererImplementation::_cascadedShadowLightHeightScaleLocation;
|
||||
int DefaultMetavoxelRendererImplementation::_shadowLightDistancesLocation;
|
||||
ProgramObject DefaultMetavoxelRendererImplementation::_heightfieldCursorProgram;
|
||||
|
||||
static void enableClipPlane(GLenum plane, float x, float y, float z, float w) {
|
||||
|
|
|
@ -140,7 +140,8 @@ public:
|
|||
static const int HEIGHT_EXTENSION;
|
||||
|
||||
HeightfieldBuffer(const glm::vec3& translation, float scale, const QByteArray& height,
|
||||
const QByteArray& color, const QByteArray& texture);
|
||||
const QByteArray& color, const QByteArray& texture = QByteArray(),
|
||||
const QVector<SharedObjectPointer>& textures = QVector<SharedObjectPointer>());
|
||||
~HeightfieldBuffer();
|
||||
|
||||
const glm::vec3& getTranslation() const { return _translation; }
|
||||
|
@ -148,7 +149,6 @@ public:
|
|||
|
||||
const Box& getHeightBounds() const { return _heightBounds; }
|
||||
const Box& getColorBounds() const { return _colorBounds; }
|
||||
const Box& getTextureBounds() const { return _textureBounds; }
|
||||
|
||||
QByteArray& getHeight() { return _height; }
|
||||
const QByteArray& getHeight() const { return _height; }
|
||||
|
@ -159,6 +159,8 @@ public:
|
|||
QByteArray& getTexture() { return _texture; }
|
||||
const QByteArray& getTexture() const { return _texture; }
|
||||
|
||||
const QVector<SharedObjectPointer>& getTextures() const { return _textures; }
|
||||
|
||||
QByteArray getUnextendedHeight() const;
|
||||
QByteArray getUnextendedColor() const;
|
||||
|
||||
|
@ -168,9 +170,6 @@ public:
|
|||
int getColorSize() const { return _colorSize; }
|
||||
float getColorIncrement() const { return _colorIncrement; }
|
||||
|
||||
int getTextureSize() const { return _textureSize; }
|
||||
float getTextureIncrement() const { return _textureIncrement; }
|
||||
|
||||
virtual void render(bool cursor = false);
|
||||
|
||||
private:
|
||||
|
@ -179,19 +178,18 @@ private:
|
|||
float _scale;
|
||||
Box _heightBounds;
|
||||
Box _colorBounds;
|
||||
Box _textureBounds;
|
||||
QByteArray _height;
|
||||
QByteArray _color;
|
||||
QByteArray _texture;
|
||||
QVector<SharedObjectPointer> _textures;
|
||||
GLuint _heightTextureID;
|
||||
GLuint _colorTextureID;
|
||||
GLuint _textureTextureID;
|
||||
QVector<NetworkTexturePointer> _networkTextures;
|
||||
int _heightSize;
|
||||
float _heightIncrement;
|
||||
int _colorSize;
|
||||
float _colorIncrement;
|
||||
int _textureSize;
|
||||
float _textureIncrement;
|
||||
|
||||
typedef QPair<QOpenGLBuffer, QOpenGLBuffer> BufferPair;
|
||||
static QHash<int, BufferPair> _bufferPairs;
|
||||
|
@ -244,6 +242,23 @@ public:
|
|||
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; }
|
||||
|
||||
static ProgramObject& getSplatHeightfieldProgram() { return _splatHeightfieldProgram; }
|
||||
static int getSplatHeightScaleLocation() { return _splatHeightScaleLocation; }
|
||||
static int getSplatTextureScaleLocation() { return _splatTextureScaleLocation; }
|
||||
|
||||
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 ProgramObject& getHeightfieldCursorProgram() { return _heightfieldCursorProgram; }
|
||||
|
||||
Q_INVOKABLE DefaultMetavoxelRendererImplementation();
|
||||
|
@ -270,6 +285,24 @@ private:
|
|||
static int _cascadedShadowMapColorScaleLocation;
|
||||
static int _shadowDistancesLocation;
|
||||
|
||||
static ProgramObject _baseHeightfieldProgram;
|
||||
static int _baseHeightScaleLocation;
|
||||
static int _baseColorScaleLocation;
|
||||
|
||||
static ProgramObject _splatHeightfieldProgram;
|
||||
static int _splatHeightScaleLocation;
|
||||
static int _splatTextureScaleLocation;
|
||||
|
||||
static ProgramObject _lightHeightfieldProgram;
|
||||
static int _lightHeightScaleLocation;
|
||||
|
||||
static ProgramObject _shadowLightHeightfieldProgram;
|
||||
static int _shadowLightHeightScaleLocation;
|
||||
|
||||
static ProgramObject _cascadedShadowLightHeightfieldProgram;
|
||||
static int _cascadedShadowLightHeightScaleLocation;
|
||||
static int _shadowLightDistancesLocation;
|
||||
|
||||
static ProgramObject _heightfieldCursorProgram;
|
||||
};
|
||||
|
||||
|
|
|
@ -179,15 +179,14 @@ public:
|
|||
const QByteArray& content;
|
||||
};
|
||||
|
||||
QSharedPointer<NetworkTexture> TextureCache::getTexture(const QUrl& url, bool normalMap,
|
||||
bool dilatable, const QByteArray& content) {
|
||||
NetworkTexturePointer TextureCache::getTexture(const QUrl& url, bool normalMap, bool dilatable, const QByteArray& content) {
|
||||
if (!dilatable) {
|
||||
TextureExtra extra = { normalMap, content };
|
||||
return ResourceCache::getResource(url, QUrl(), false, &extra).staticCast<NetworkTexture>();
|
||||
}
|
||||
QSharedPointer<NetworkTexture> texture = _dilatableNetworkTextures.value(url);
|
||||
NetworkTexturePointer texture = _dilatableNetworkTextures.value(url);
|
||||
if (texture.isNull()) {
|
||||
texture = QSharedPointer<NetworkTexture>(new DilatableNetworkTexture(url, content), &Resource::allReferencesCleared);
|
||||
texture = NetworkTexturePointer(new DilatableNetworkTexture(url, content), &Resource::allReferencesCleared);
|
||||
texture->setSelf(texture);
|
||||
texture->setCache(this);
|
||||
_dilatableNetworkTextures.insert(url, texture);
|
||||
|
|
|
@ -23,6 +23,8 @@ class QOpenGLFramebufferObject;
|
|||
|
||||
class NetworkTexture;
|
||||
|
||||
typedef QSharedPointer<NetworkTexture> NetworkTexturePointer;
|
||||
|
||||
/// Stores cached textures, including render-to-texture targets.
|
||||
class TextureCache : public ResourceCache {
|
||||
Q_OBJECT
|
||||
|
@ -47,7 +49,7 @@ public:
|
|||
GLuint getBlueTextureID();
|
||||
|
||||
/// Loads a texture from the specified URL.
|
||||
QSharedPointer<NetworkTexture> getTexture(const QUrl& url, bool normalMap = false, bool dilatable = false,
|
||||
NetworkTexturePointer getTexture(const QUrl& url, bool normalMap = false, bool dilatable = false,
|
||||
const QByteArray& content = QByteArray());
|
||||
|
||||
/// Returns a pointer to the primary framebuffer object. This render target includes a depth component, and is
|
||||
|
|
|
@ -974,7 +974,8 @@ void ImportHeightfieldTool::apply() {
|
|||
data.setRoot(AttributeRegistry::getInstance()->getHeightfieldColorAttribute(), new MetavoxelNode(AttributeValue(
|
||||
AttributeRegistry::getInstance()->getHeightfieldColorAttribute(), encodeInline(colorPointer))));
|
||||
|
||||
QByteArray texture(height.size(), 0);
|
||||
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))));
|
||||
|
@ -1049,8 +1050,7 @@ void ImportHeightfieldTool::updatePreview() {
|
|||
columns * HeightfieldData::COLOR_BYTES);
|
||||
}
|
||||
}
|
||||
buffers.append(BufferDataPointer(new HeightfieldBuffer(glm::vec3(x, 0.0f, z), 1.0f,
|
||||
height, color, QByteArray())));
|
||||
buffers.append(BufferDataPointer(new HeightfieldBuffer(glm::vec3(x, 0.0f, z), 1.0f, height, color)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -593,7 +593,7 @@ int PaintHeightfieldTextureEditVisitor::visit(MetavoxelInfo& info) {
|
|||
}
|
||||
int size = glm::sqrt((float)contents.size());
|
||||
int highest = size - 1;
|
||||
float heightScale = size / info.size;
|
||||
float heightScale = highest / info.size;
|
||||
|
||||
glm::vec3 center = (_edit.position - info.minimum) * heightScale;
|
||||
float scaledRadius = _edit.radius * heightScale;
|
||||
|
|
Loading…
Reference in a new issue