Improved the opengl terrain rendering queue
This commit is contained in:
parent
eb795b0f83
commit
1131972759
8 changed files with 144 additions and 127 deletions
|
@ -12,26 +12,27 @@
|
||||||
ExplorerChunkTerrain::ExplorerChunkTerrain(OpenGLRenderer* renderer, double x, double z, double size, int nbchunks, double water_height):
|
ExplorerChunkTerrain::ExplorerChunkTerrain(OpenGLRenderer* renderer, double x, double z, double size, int nbchunks, double water_height):
|
||||||
_renderer(renderer)
|
_renderer(renderer)
|
||||||
{
|
{
|
||||||
_color_profile = new ColorProfile(ColorProfile::TONE_MAPPING_REIHNARD, 2.0);
|
|
||||||
|
|
||||||
priority = 0.0;
|
priority = 0.0;
|
||||||
_reset_needed = false;
|
_reset_needed = false;
|
||||||
|
|
||||||
|
interrupt = false;
|
||||||
|
|
||||||
_texture = new QImage(1, 1, QImage::Format_RGBA8888);
|
_texture = new QImage(1, 1, QImage::Format_RGBA8888);
|
||||||
texture_id = 0;
|
texture_id = 0;
|
||||||
_texture_changed = false;
|
_texture_changed = false;
|
||||||
_texture_current_size = 0;
|
_texture_current_size = 0;
|
||||||
_texture_max_size = 0;
|
_texture_wanted_size = 0;
|
||||||
|
_texture_max_size = 256;
|
||||||
|
|
||||||
_startx = x;
|
_startx = x;
|
||||||
_startz = z;
|
_startz = z;
|
||||||
_size = size;
|
_size = size;
|
||||||
_overall_step = size * (double) nbchunks;
|
_overall_step = size * (double) nbchunks;
|
||||||
|
|
||||||
_distance_to_camera = 0.0;
|
distance_to_camera = 0.0;
|
||||||
|
|
||||||
_water_height = water_height;
|
_water_height = water_height;
|
||||||
_overwater = false;
|
overwater = false;
|
||||||
|
|
||||||
tessellation_count = 33;
|
tessellation_count = 33;
|
||||||
tessellated = new VertexArray<TerrainVertex>();
|
tessellated = new VertexArray<TerrainVertex>();
|
||||||
|
@ -41,15 +42,12 @@ ExplorerChunkTerrain::ExplorerChunkTerrain(OpenGLRenderer* renderer, double x, d
|
||||||
_tessellation_current_size = 0;
|
_tessellation_current_size = 0;
|
||||||
_tessellation_step = _size / (double) _tessellation_max_size;
|
_tessellation_step = _size / (double) _tessellation_max_size;
|
||||||
|
|
||||||
setMaxTextureSize(128);
|
|
||||||
|
|
||||||
maintain();
|
maintain();
|
||||||
}
|
}
|
||||||
|
|
||||||
ExplorerChunkTerrain::~ExplorerChunkTerrain()
|
ExplorerChunkTerrain::~ExplorerChunkTerrain()
|
||||||
{
|
{
|
||||||
_lock_data.lock();
|
_lock_data.lock();
|
||||||
delete _color_profile;
|
|
||||||
delete _texture;
|
delete _texture;
|
||||||
delete tessellated;
|
delete tessellated;
|
||||||
_lock_data.unlock();
|
_lock_data.unlock();
|
||||||
|
@ -65,14 +63,71 @@ bool ExplorerChunkTerrain::maintain()
|
||||||
_reset_needed = false;
|
_reset_needed = false;
|
||||||
_texture_current_size = 0;
|
_texture_current_size = 0;
|
||||||
_tessellation_current_size = 0;
|
_tessellation_current_size = 0;
|
||||||
_overwater = false;
|
overwater = false;
|
||||||
}
|
}
|
||||||
_lock_data.unlock();
|
_lock_data.unlock();
|
||||||
|
|
||||||
subchanged = onMaintainEvent();
|
// Improve heightmap resolution
|
||||||
|
if (_tessellation_current_size < _tessellation_max_size)
|
||||||
|
{
|
||||||
|
while (_tessellation_current_size < _tessellation_max_size)
|
||||||
|
{
|
||||||
|
int new_tessellation_size = _tessellation_current_size ? _tessellation_current_size * 4 : 2;
|
||||||
|
int old_tessellation_inc = _tessellation_current_size ? _tessellation_max_size / _tessellation_current_size : 1;
|
||||||
|
int new_tessellation_inc = _tessellation_max_size / new_tessellation_size;
|
||||||
|
float internal_step = 1.0f / (float)_tessellation_max_size;
|
||||||
|
for (int j = 0; j <= _tessellation_max_size; j += new_tessellation_inc)
|
||||||
|
{
|
||||||
|
for (int i = 0; i <= _tessellation_max_size; i += new_tessellation_inc)
|
||||||
|
{
|
||||||
|
if (_tessellation_current_size == 0 || i % old_tessellation_inc != 0 || j % old_tessellation_inc != 0)
|
||||||
|
{
|
||||||
|
double x = _startx + _tessellation_step * (float)i;
|
||||||
|
double z = _startz + _tessellation_step * (float)j;
|
||||||
|
|
||||||
|
double height = _renderer->getTerrainRenderer()->getHeight(x, z, 1);
|
||||||
|
if (height >= _water_height)
|
||||||
|
{
|
||||||
|
overwater = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
TerrainVertex v;
|
||||||
|
|
||||||
|
v.uv[0] = internal_step * (float)i;
|
||||||
|
v.uv[1] = internal_step * (float)j;
|
||||||
|
|
||||||
|
v.location[0] = x;
|
||||||
|
v.location[1] = height;
|
||||||
|
v.location[2] = z;
|
||||||
|
|
||||||
|
tessellated->setGridVertex(tessellation_count, i, j, v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (interrupt or _reset_needed)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_lock_data.lock();
|
||||||
|
_tessellation_current_size = new_tessellation_size;
|
||||||
|
tessellated->setAutoGridIndices(tessellation_count, new_tessellation_inc);
|
||||||
|
_lock_data.unlock();
|
||||||
|
|
||||||
|
if (_tessellation_current_size >= 4)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
subchanged = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
subchanged = false;
|
||||||
|
}
|
||||||
|
|
||||||
// Improve texture resolution
|
// Improve texture resolution
|
||||||
if (_texture_current_size < _texture_max_size)
|
if (_texture_current_size < _texture_wanted_size)
|
||||||
{
|
{
|
||||||
int new_texture_size = _texture_current_size ? _texture_current_size * 2 : 1;
|
int new_texture_size = _texture_current_size ? _texture_current_size * 2 : 1;
|
||||||
QImage* new_image = new QImage(_texture->scaled(new_texture_size + 1, new_texture_size + 1, Qt::IgnoreAspectRatio, Qt::FastTransformation));
|
QImage* new_image = new QImage(_texture->scaled(new_texture_size + 1, new_texture_size + 1, Qt::IgnoreAspectRatio, Qt::FastTransformation));
|
||||||
|
@ -83,11 +138,15 @@ bool ExplorerChunkTerrain::maintain()
|
||||||
if (_texture_current_size <= 1 || i % 2 != 0 || j % 2 != 0)
|
if (_texture_current_size <= 1 || i % 2 != 0 || j % 2 != 0)
|
||||||
{
|
{
|
||||||
Color color = getTextureColor((double)i / (double)new_texture_size, (double)j / (double)new_texture_size);
|
Color color = getTextureColor((double)i / (double)new_texture_size, (double)j / (double)new_texture_size);
|
||||||
//color = _color_profile->apply(color);
|
|
||||||
color.normalize();
|
color.normalize();
|
||||||
new_image->setPixel(i, j, color.to32BitRGBA());
|
new_image->setPixel(i, j, color.to32BitRGBA());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (interrupt or _reset_needed)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_lock_data.lock();
|
_lock_data.lock();
|
||||||
|
@ -97,11 +156,6 @@ bool ExplorerChunkTerrain::maintain()
|
||||||
_texture_changed = true;
|
_texture_changed = true;
|
||||||
_lock_data.unlock();
|
_lock_data.unlock();
|
||||||
|
|
||||||
/*if (_texture_current_size < 4 && _texture_current_size < _texture_max_size)
|
|
||||||
{
|
|
||||||
maintain();
|
|
||||||
}*/
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -110,77 +164,8 @@ bool ExplorerChunkTerrain::maintain()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ExplorerChunkTerrain::onMaintainEvent()
|
|
||||||
{
|
|
||||||
// Improve heightmap resolution
|
|
||||||
if (_tessellation_current_size < _tessellation_max_size)
|
|
||||||
{
|
|
||||||
int new_tessellation_size = _tessellation_current_size ? _tessellation_current_size * 4 : 2;
|
|
||||||
int old_tessellation_inc = _tessellation_current_size ? _tessellation_max_size / _tessellation_current_size : 1;
|
|
||||||
int new_tessellation_inc = _tessellation_max_size / new_tessellation_size;
|
|
||||||
float internal_step = 1.0f / (float)_tessellation_max_size;
|
|
||||||
for (int j = 0; j <= _tessellation_max_size; j += new_tessellation_inc)
|
|
||||||
{
|
|
||||||
for (int i = 0; i <= _tessellation_max_size; i += new_tessellation_inc)
|
|
||||||
{
|
|
||||||
if (_tessellation_current_size == 0 || i % old_tessellation_inc != 0 || j % old_tessellation_inc != 0)
|
|
||||||
{
|
|
||||||
double x = _startx + _tessellation_step * (float)i;
|
|
||||||
double z = _startz + _tessellation_step * (float)j;
|
|
||||||
|
|
||||||
double height = _renderer->getTerrainRenderer()->getHeight(x, z, 1);
|
|
||||||
if (height >= _water_height)
|
|
||||||
{
|
|
||||||
_overwater = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
TerrainVertex v;
|
|
||||||
|
|
||||||
v.uv[0] = internal_step * (float)i;
|
|
||||||
v.uv[1] = internal_step * (float)j;
|
|
||||||
|
|
||||||
v.location[0] = x;
|
|
||||||
v.location[1] = height;
|
|
||||||
v.location[2] = z;
|
|
||||||
|
|
||||||
tessellated->setGridVertex(tessellation_count, i, j, v);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_lock_data.lock();
|
|
||||||
_tessellation_current_size = new_tessellation_size;
|
|
||||||
tessellated->setAutoGridIndices(tessellation_count, new_tessellation_inc);
|
|
||||||
_lock_data.unlock();
|
|
||||||
|
|
||||||
if (_tessellation_current_size < 4 && _tessellation_current_size < _tessellation_max_size)
|
|
||||||
{
|
|
||||||
onMaintainEvent();
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ExplorerChunkTerrain::updatePriority(CameraDefinition* camera)
|
void ExplorerChunkTerrain::updatePriority(CameraDefinition* camera)
|
||||||
{
|
{
|
||||||
if (_reset_needed || (_texture_max_size > 1 && _texture_current_size <= 1))
|
|
||||||
{
|
|
||||||
priority = 1000.0;
|
|
||||||
}
|
|
||||||
else if (_texture_current_size == _texture_max_size)
|
|
||||||
{
|
|
||||||
priority = -1000.0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
priority = getDisplayedSizeHint(camera) - _texture_current_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
Vector3 camera_location = camera->getLocation();
|
Vector3 camera_location = camera->getLocation();
|
||||||
|
|
||||||
// Handle position
|
// Handle position
|
||||||
|
@ -205,10 +190,45 @@ void ExplorerChunkTerrain::updatePriority(CameraDefinition* camera)
|
||||||
_startz -= _overall_step;
|
_startz -= _overall_step;
|
||||||
askReset();
|
askReset();
|
||||||
}
|
}
|
||||||
|
distance_to_camera = getCenter().sub(camera_location).getNorm();
|
||||||
_distance_to_camera = getCenter().sub(camera_location).getNorm();
|
|
||||||
|
|
||||||
_lock_data.unlock();
|
_lock_data.unlock();
|
||||||
|
|
||||||
|
// Update wanted LOD
|
||||||
|
if (not overwater)
|
||||||
|
{
|
||||||
|
_texture_wanted_size = 2;
|
||||||
|
}
|
||||||
|
else if (distance_to_camera < 50.0)
|
||||||
|
{
|
||||||
|
_texture_wanted_size = _texture_max_size;
|
||||||
|
}
|
||||||
|
else if (distance_to_camera < 100.0)
|
||||||
|
{
|
||||||
|
_texture_wanted_size = _texture_max_size / 4;
|
||||||
|
}
|
||||||
|
else if (distance_to_camera < 200.0)
|
||||||
|
{
|
||||||
|
_texture_wanted_size = _texture_max_size / 8;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_texture_wanted_size = 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update priority
|
||||||
|
if (_reset_needed || (_texture_max_size > 1 && _texture_current_size <= 1))
|
||||||
|
{
|
||||||
|
priority = 1000.0;
|
||||||
|
}
|
||||||
|
else if (_texture_current_size == _texture_wanted_size)
|
||||||
|
{
|
||||||
|
priority = -1000.0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
priority = _texture_wanted_size / _texture_current_size;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExplorerChunkTerrain::render(QOpenGLShaderProgram* program, OpenGLFunctions* functions)
|
void ExplorerChunkTerrain::render(QOpenGLShaderProgram* program, OpenGLFunctions* functions)
|
||||||
|
@ -245,19 +265,18 @@ void ExplorerChunkTerrain::render(QOpenGLShaderProgram* program, OpenGLFunctions
|
||||||
int tessellation_size = _tessellation_current_size;
|
int tessellation_size = _tessellation_current_size;
|
||||||
_lock_data.unlock();
|
_lock_data.unlock();
|
||||||
|
|
||||||
if (tessellation_size <= 1 or not _overwater)
|
if (tessellation_size <= 1 or not overwater)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_lock_data.lock(); // TEMP
|
_lock_data.lock();
|
||||||
// TEMP
|
// TEMP
|
||||||
functions->glActiveTexture(GL_TEXTURE0 + 3);
|
functions->glActiveTexture(GL_TEXTURE0 + 3);
|
||||||
functions->glBindTexture(GL_TEXTURE_2D, texture_id);
|
functions->glBindTexture(GL_TEXTURE_2D, texture_id);
|
||||||
program->setUniformValue("groundTexture", 3);
|
program->setUniformValue("groundTexture", 3);
|
||||||
|
|
||||||
tessellated->render(program, functions);
|
tessellated->render(program, functions);
|
||||||
_lock_data.unlock(); // TEMP
|
_lock_data.unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -266,33 +285,9 @@ void ExplorerChunkTerrain::askReset()
|
||||||
_reset_needed = true;
|
_reset_needed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExplorerChunkTerrain::setMaxTextureSize(int size)
|
void ExplorerChunkTerrain::askInterrupt()
|
||||||
{
|
{
|
||||||
_texture_max_size = size;
|
interrupt = true;
|
||||||
}
|
|
||||||
|
|
||||||
double ExplorerChunkTerrain::getDisplayedSizeHint(CameraDefinition* camera)
|
|
||||||
{
|
|
||||||
double distance;
|
|
||||||
Vector3 center;
|
|
||||||
|
|
||||||
if (not _overwater)
|
|
||||||
{
|
|
||||||
return -1000.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
center = getCenter();
|
|
||||||
|
|
||||||
if (camera->isBoxInView(center, _size, 40.0, _size))
|
|
||||||
{
|
|
||||||
distance = _distance_to_camera;
|
|
||||||
distance = distance < 0.1 ? 0.1 : distance;
|
|
||||||
return (int) ceil(120.0 - distance / 1.5);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return -800.0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Color ExplorerChunkTerrain::getTextureColor(double x, double y)
|
Color ExplorerChunkTerrain::getTextureColor(double x, double y)
|
||||||
|
|
|
@ -28,10 +28,8 @@ public:
|
||||||
void render(QOpenGLShaderProgram* program, OpenGLFunctions* functions);
|
void render(QOpenGLShaderProgram* program, OpenGLFunctions* functions);
|
||||||
|
|
||||||
void askReset();
|
void askReset();
|
||||||
void setMaxTextureSize(int size);
|
void askInterrupt();
|
||||||
|
|
||||||
bool onMaintainEvent();
|
|
||||||
double getDisplayedSizeHint(CameraDefinition* camera);
|
|
||||||
Color getTextureColor(double x, double y);
|
Color getTextureColor(double x, double y);
|
||||||
|
|
||||||
double priority;
|
double priority;
|
||||||
|
@ -44,9 +42,7 @@ private:
|
||||||
double _size;
|
double _size;
|
||||||
double _overall_step;
|
double _overall_step;
|
||||||
|
|
||||||
double _distance_to_camera;
|
|
||||||
double _water_height;
|
double _water_height;
|
||||||
bool _overwater;
|
|
||||||
|
|
||||||
int tessellation_count;
|
int tessellation_count;
|
||||||
VertexArray<TerrainVertex> *tessellated;
|
VertexArray<TerrainVertex> *tessellated;
|
||||||
|
@ -57,15 +53,20 @@ private:
|
||||||
QMutex _lock_data;
|
QMutex _lock_data;
|
||||||
|
|
||||||
OpenGLRenderer* _renderer;
|
OpenGLRenderer* _renderer;
|
||||||
ColorProfile* _color_profile;
|
|
||||||
|
|
||||||
bool _reset_needed;
|
bool _reset_needed;
|
||||||
|
bool interrupt;
|
||||||
|
|
||||||
QImage* _texture;
|
QImage* _texture;
|
||||||
unsigned int texture_id;
|
unsigned int texture_id;
|
||||||
bool _texture_changed;
|
bool _texture_changed;
|
||||||
int _texture_current_size;
|
int _texture_current_size;
|
||||||
|
int _texture_wanted_size;
|
||||||
int _texture_max_size;
|
int _texture_max_size;
|
||||||
|
|
||||||
|
// LOD control
|
||||||
|
double distance_to_camera;
|
||||||
|
bool overwater;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,10 @@ OpenGLPart::~OpenGLPart()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void OpenGLPart::interrupt()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
OpenGLShaderProgram* OpenGLPart::createShader(QString name)
|
OpenGLShaderProgram* OpenGLPart::createShader(QString name)
|
||||||
{
|
{
|
||||||
OpenGLShaderProgram* program = new OpenGLShaderProgram(name, renderer);
|
OpenGLShaderProgram* program = new OpenGLShaderProgram(name, renderer);
|
||||||
|
|
|
@ -26,6 +26,9 @@ public:
|
||||||
// Do the rendering
|
// Do the rendering
|
||||||
virtual void render() = 0;
|
virtual void render() = 0;
|
||||||
|
|
||||||
|
// Interrupt the rendering
|
||||||
|
virtual void interrupt();
|
||||||
|
|
||||||
void updateScenery(bool onlyCommon=false);
|
void updateScenery(bool onlyCommon=false);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
|
@ -32,6 +32,10 @@ OpenGLRenderer::OpenGLRenderer(Scenery* scenery):
|
||||||
|
|
||||||
OpenGLRenderer::~OpenGLRenderer()
|
OpenGLRenderer::~OpenGLRenderer()
|
||||||
{
|
{
|
||||||
|
terrain->interrupt();
|
||||||
|
water->interrupt();
|
||||||
|
skybox->interrupt();
|
||||||
|
|
||||||
delete skybox;
|
delete skybox;
|
||||||
delete water;
|
delete water;
|
||||||
delete terrain;
|
delete terrain;
|
||||||
|
|
|
@ -58,7 +58,7 @@ void OpenGLTerrain::initialize()
|
||||||
program->addFragmentSource("terrain");
|
program->addFragmentSource("terrain");
|
||||||
|
|
||||||
// Add terrain chunks
|
// Add terrain chunks
|
||||||
int chunks = 25;
|
int chunks = 16;
|
||||||
double size = 800.0;
|
double size = 800.0;
|
||||||
double chunksize = size / (double) chunks;
|
double chunksize = size / (double) chunks;
|
||||||
double start = -size / 2.0;
|
double start = -size / 2.0;
|
||||||
|
@ -93,6 +93,14 @@ void OpenGLTerrain::render()
|
||||||
program->release();
|
program->release();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void OpenGLTerrain::interrupt()
|
||||||
|
{
|
||||||
|
for (auto chunk: _chunks)
|
||||||
|
{
|
||||||
|
chunk->askInterrupt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static bool _cmpChunks(const ExplorerChunkTerrain* c1, const ExplorerChunkTerrain* c2)
|
static bool _cmpChunks(const ExplorerChunkTerrain* c1, const ExplorerChunkTerrain* c2)
|
||||||
{
|
{
|
||||||
return c1->priority > c2->priority;
|
return c1->priority > c2->priority;
|
||||||
|
|
|
@ -21,6 +21,7 @@ public:
|
||||||
virtual void initialize() override;
|
virtual void initialize() override;
|
||||||
virtual void update() override;
|
virtual void update() override;
|
||||||
virtual void render() override;
|
virtual void render() override;
|
||||||
|
virtual void interrupt() override;
|
||||||
|
|
||||||
void performChunksMaintenance();
|
void performChunksMaintenance();
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,7 @@ QGLWidget(parent)
|
||||||
camera->copy(_current_camera);
|
camera->copy(_current_camera);
|
||||||
|
|
||||||
_renderer = new OpenGLRenderer(scenery);
|
_renderer = new OpenGLRenderer(scenery);
|
||||||
|
scenery->setCamera(_current_camera);
|
||||||
|
|
||||||
_average_frame_time = 0.05;
|
_average_frame_time = 0.05;
|
||||||
_quality = 3;
|
_quality = 3;
|
||||||
|
@ -130,7 +131,7 @@ void WidgetExplorer::mouseMoveEvent(QMouseEvent* event)
|
||||||
{
|
{
|
||||||
factor = 0.01;
|
factor = 0.01;
|
||||||
}
|
}
|
||||||
else if (event->modifiers() & Qt::ShiftModifier)
|
else if ((event->modifiers() & Qt::ShiftModifier) and not (event->buttons() & Qt::LeftButton))
|
||||||
{
|
{
|
||||||
factor = 1.0;
|
factor = 1.0;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue