379 lines
13 KiB
C++
379 lines
13 KiB
C++
// SPDX-License-Identifier: LGPL-2.1-or-later
|
|
|
|
/***************************************************************************
|
|
* Copyright (c) 2023 Werner Mayer <wmayer[at]users.sourceforge.net> *
|
|
* *
|
|
* This file is part of FreeCAD. *
|
|
* *
|
|
* FreeCAD is free software: you can redistribute it and/or modify it *
|
|
* under the terms of the GNU Lesser General Public License as *
|
|
* published by the Free Software Foundation, either version 2.1 of the *
|
|
* License, or (at your option) any later version. *
|
|
* *
|
|
* FreeCAD is distributed in the hope that it will be useful, but *
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
|
* Lesser General Public License for more details. *
|
|
* *
|
|
* You should have received a copy of the GNU Lesser General Public *
|
|
* License along with FreeCAD. If not, see *
|
|
* <https://www.gnu.org/licenses/>. *
|
|
* *
|
|
**************************************************************************/
|
|
|
|
#include <Inventor/events/SoEvent.h>
|
|
#include <Inventor/nodes/SoDirectionalLight.h>
|
|
#include <Inventor/nodes/SoEventCallback.h>
|
|
#include <Inventor/nodes/SoOrthographicCamera.h>
|
|
#include <Inventor/nodes/SoComplexity.h>
|
|
#include <Inventor/nodes/SoSeparator.h>
|
|
#include <Inventor/nodes/SoMaterial.h>
|
|
#include <Inventor/nodes/SoSphere.h>
|
|
|
|
#include "DlgSettingsLightSources.h"
|
|
#include "ui_DlgSettingsLightSources.h"
|
|
|
|
#include <Utilities.h>
|
|
#include <Base/Builder3D.h>
|
|
#include <Base/Tools.h>
|
|
#include <Gui/View3DInventorViewer.h>
|
|
#include <Gui/View3DSettings.h>
|
|
|
|
|
|
using namespace Gui::Dialog;
|
|
|
|
/* TRANSLATOR Gui::Dialog::DlgSettingsLightSources */
|
|
|
|
DlgSettingsLightSources::DlgSettingsLightSources(QWidget* parent)
|
|
: PreferencePage(parent)
|
|
, ui(new Ui_DlgSettingsLightSources)
|
|
{
|
|
ui->setupUi(this);
|
|
|
|
view = ui->viewer;
|
|
|
|
configureViewer();
|
|
|
|
const auto connectLightEvents = [this](
|
|
QuantitySpinBox* horizontalAngleSpinBox,
|
|
QuantitySpinBox* verticalAngleSpinBox,
|
|
QSpinBox* intensitySpinBox,
|
|
ColorButton* colorButton,
|
|
QCheckBox* enabledCheckbox,
|
|
auto updateLightFunction
|
|
) {
|
|
connect(
|
|
horizontalAngleSpinBox,
|
|
qOverload<double>(&QuantitySpinBox::valueChanged),
|
|
this,
|
|
updateLightFunction
|
|
);
|
|
connect(
|
|
verticalAngleSpinBox,
|
|
qOverload<double>(&QuantitySpinBox::valueChanged),
|
|
this,
|
|
updateLightFunction
|
|
);
|
|
connect(intensitySpinBox, qOverload<int>(&QSpinBox::valueChanged), this, updateLightFunction);
|
|
connect(colorButton, &ColorButton::changed, this, updateLightFunction);
|
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0)
|
|
connect(enabledCheckbox, &QCheckBox::checkStateChanged, this, updateLightFunction);
|
|
#else
|
|
connect(enabledCheckbox, &QCheckBox::stateChanged, this, updateLightFunction);
|
|
#endif
|
|
};
|
|
|
|
const auto updateLight = [this](
|
|
SoDirectionalLight* light,
|
|
QuantitySpinBox* horizontalAngleSpinBox,
|
|
QuantitySpinBox* verticalAngleSpinBox,
|
|
QSpinBox* intensitySpinBox,
|
|
ColorButton* colorButton,
|
|
QCheckBox* enabledCheckbox,
|
|
std::function<void(bool)> setLightEnabled
|
|
) {
|
|
light->color = Base::convertTo<SbColor>(colorButton->color());
|
|
light->intensity = Base::fromPercent(intensitySpinBox->value());
|
|
light->direction = Base::convertTo<SbVec3f>(azimuthElevationToDirection(
|
|
horizontalAngleSpinBox->rawValue(),
|
|
verticalAngleSpinBox->rawValue()
|
|
));
|
|
setLightEnabled(enabledCheckbox->isChecked());
|
|
};
|
|
|
|
const auto updateHeadLight = [this, updateLight] {
|
|
updateLight(
|
|
view->getHeadlight(),
|
|
ui->mainLightHorizontalAngle,
|
|
ui->mainLightVerticalAngle,
|
|
ui->mainLightIntensitySpinBox,
|
|
ui->mainLightColor,
|
|
ui->mainLightEnable,
|
|
[this](bool enabled) { view->setHeadlightEnabled(enabled); }
|
|
);
|
|
};
|
|
|
|
const auto updateFillLight = [this, updateLight] {
|
|
updateLight(
|
|
view->getFillLight(),
|
|
ui->fillLightHorizontalAngle,
|
|
ui->fillLightVerticalAngle,
|
|
ui->fillLightIntensitySpinBox,
|
|
ui->fillLightColor,
|
|
ui->fillLightEnable,
|
|
[this](bool enabled) { view->setFillLightEnabled(enabled); }
|
|
);
|
|
};
|
|
|
|
const auto updateBackLight = [this, updateLight] {
|
|
updateLight(
|
|
view->getBacklight(),
|
|
ui->backLightHorizontalAngle,
|
|
ui->backLightVerticalAngle,
|
|
ui->backLightIntensitySpinBox,
|
|
ui->backLightColor,
|
|
ui->backLightEnable,
|
|
[this](bool enabled) { view->setBacklightEnabled(enabled); }
|
|
);
|
|
};
|
|
|
|
connectLightEvents(
|
|
ui->mainLightHorizontalAngle,
|
|
ui->mainLightVerticalAngle,
|
|
ui->mainLightIntensitySpinBox,
|
|
ui->mainLightColor,
|
|
ui->mainLightEnable,
|
|
updateHeadLight
|
|
);
|
|
connectLightEvents(
|
|
ui->backLightHorizontalAngle,
|
|
ui->backLightVerticalAngle,
|
|
ui->backLightIntensitySpinBox,
|
|
ui->backLightColor,
|
|
ui->backLightEnable,
|
|
updateBackLight
|
|
);
|
|
connectLightEvents(
|
|
ui->fillLightHorizontalAngle,
|
|
ui->fillLightVerticalAngle,
|
|
ui->fillLightIntensitySpinBox,
|
|
ui->fillLightColor,
|
|
ui->fillLightEnable,
|
|
updateFillLight
|
|
);
|
|
|
|
const auto updateAmbientLight = [this] {
|
|
view->getEnvironment()->ambientColor = Base::convertTo<SbColor>(ui->ambientLightColor->color());
|
|
view->getEnvironment()->ambientIntensity = Base::fromPercent(
|
|
ui->ambientLightIntensitySpinBox->value()
|
|
);
|
|
};
|
|
|
|
connect(
|
|
ui->ambientLightIntensitySpinBox,
|
|
qOverload<int>(&QSpinBox::valueChanged),
|
|
this,
|
|
updateAmbientLight
|
|
);
|
|
connect(ui->ambientLightColor, &ColorButton::changed, this, updateAmbientLight);
|
|
|
|
connect(ui->zoomInButton, &QToolButton::clicked, this, &DlgSettingsLightSources::zoomIn);
|
|
connect(ui->zoomOutButton, &QToolButton::clicked, this, &DlgSettingsLightSources::zoomOut);
|
|
|
|
DlgSettingsLightSources::loadSettings();
|
|
}
|
|
|
|
static inline SoMaterial* createMaterial(void)
|
|
{
|
|
const QColor ambientColor {0xff333333}, diffuseColor {0xffd2d2ff}, emissiveColor {0xff000000},
|
|
specularColor {0xffcccccc};
|
|
|
|
auto material = new SoMaterial();
|
|
|
|
material->ambientColor.setValue(Base::convertTo<SbColor>(ambientColor));
|
|
material->diffuseColor.setValue(Base::convertTo<SbColor>(diffuseColor));
|
|
material->emissiveColor.setValue(Base::convertTo<SbColor>(emissiveColor));
|
|
material->specularColor.setValue(Base::convertTo<SbColor>(specularColor));
|
|
|
|
material->shininess = 0.9f;
|
|
|
|
return material;
|
|
}
|
|
|
|
static inline SoSphere* createSphere(void)
|
|
{
|
|
auto sphere = new SoSphere();
|
|
sphere->radius = 3;
|
|
|
|
return sphere;
|
|
}
|
|
|
|
static inline SoComplexity* createGoodComplexity()
|
|
{
|
|
auto complexity = new SoComplexity();
|
|
complexity->value = 1.0;
|
|
|
|
return complexity;
|
|
}
|
|
|
|
void DlgSettingsLightSources::configureViewer()
|
|
{
|
|
const SbVec3f defaultViewDirection {0.0f, 1.0f, 0.3f};
|
|
|
|
View3DSettings(hGrpView, view).applySettings();
|
|
|
|
view->setRedirectToSceneGraph(true);
|
|
view->setViewing(true);
|
|
view->setPopupMenuEnabled(false);
|
|
view->setEnabledNaviCube(false);
|
|
|
|
const auto root = static_cast<SoSeparator*>(view->getSceneGraph());
|
|
root->addChild(createGoodComplexity());
|
|
root->addChild(createMaterial());
|
|
root->addChild(createSphere());
|
|
|
|
const auto callback = new SoEventCallback();
|
|
root->addChild(callback);
|
|
callback->addEventCallback(
|
|
SoEvent::getClassTypeId(),
|
|
[]([[maybe_unused]] void* ud, SoEventCallback* cb) { cb->setHandled(); }
|
|
);
|
|
|
|
view->setCameraType(SoOrthographicCamera::getClassTypeId());
|
|
view->setViewDirection(defaultViewDirection);
|
|
view->viewAll();
|
|
|
|
camera = dynamic_cast<SoOrthographicCamera*>(view->getCamera());
|
|
const float cameraHeight = camera->height.getValue() * 2.0f;
|
|
camera->height = cameraHeight;
|
|
zoomStep = cameraHeight / 14.0f;
|
|
}
|
|
|
|
Base::Vector3d DlgSettingsLightSources::azimuthElevationToDirection(double azimuth, double elevation)
|
|
{
|
|
azimuth = Base::toRadians(azimuth);
|
|
elevation = Base::toRadians(elevation);
|
|
|
|
auto direction = Base::Vector3d {
|
|
std::sin(azimuth) * std::cos(elevation),
|
|
std::cos(azimuth) * std::cos(elevation),
|
|
std::sin(elevation)
|
|
};
|
|
|
|
direction.Normalize();
|
|
|
|
return direction;
|
|
}
|
|
|
|
std::pair<double, double> DlgSettingsLightSources::directionToAzimuthElevation(Base::Vector3d direction)
|
|
{
|
|
const auto azimuth = std::atan2(direction[0], direction[1]);
|
|
const auto elevation = std::atan2(
|
|
direction[2],
|
|
std::sqrt(direction[1] * direction[1] + direction[0] * direction[0])
|
|
);
|
|
|
|
return {Base::toDegrees(azimuth), Base::toDegrees(elevation)};
|
|
}
|
|
|
|
void DlgSettingsLightSources::saveSettings()
|
|
{
|
|
for (const auto& widget : findChildren<QWidget*>()) {
|
|
if (const auto pref = dynamic_cast<PrefWidget*>(widget)) {
|
|
pref->onSave();
|
|
}
|
|
}
|
|
|
|
const auto saveAngles = [this](
|
|
QuantitySpinBox* horizontalAngleSpinBox,
|
|
QuantitySpinBox* verticalAngleSpinBox,
|
|
const char* parameter
|
|
) {
|
|
try {
|
|
const auto direction = azimuthElevationToDirection(
|
|
horizontalAngleSpinBox->rawValue(),
|
|
verticalAngleSpinBox->rawValue()
|
|
);
|
|
|
|
hGrp->SetASCII(parameter, Base::vectorToString(Base::convertTo<Base::Vector3f>(direction)));
|
|
}
|
|
catch (...) {
|
|
}
|
|
};
|
|
|
|
saveAngles(ui->mainLightHorizontalAngle, ui->mainLightVerticalAngle, "HeadlightDirection");
|
|
saveAngles(ui->backLightHorizontalAngle, ui->backLightVerticalAngle, "BacklightDirection");
|
|
saveAngles(ui->fillLightHorizontalAngle, ui->fillLightVerticalAngle, "FillLightDirection");
|
|
}
|
|
|
|
void DlgSettingsLightSources::loadSettings()
|
|
{
|
|
for (const auto& widget : findChildren<QWidget*>()) {
|
|
if (const auto pref = dynamic_cast<PrefWidget*>(widget)) {
|
|
pref->onRestore();
|
|
}
|
|
}
|
|
|
|
const auto loadAngles = [this](
|
|
QuantitySpinBox* horizontalAngleSpinBox,
|
|
QuantitySpinBox* verticalAngleSpinBox,
|
|
const char* parameter
|
|
) {
|
|
try {
|
|
const auto direction = Base::stringToVector(hGrp->GetASCII(parameter));
|
|
const auto [azimuth, elevation] = directionToAzimuthElevation(
|
|
Base::convertTo<Base::Vector3d>(direction)
|
|
);
|
|
|
|
horizontalAngleSpinBox->setValue(azimuth);
|
|
verticalAngleSpinBox->setValue(elevation);
|
|
}
|
|
catch (...) {
|
|
}
|
|
};
|
|
|
|
loadAngles(ui->mainLightHorizontalAngle, ui->mainLightVerticalAngle, "HeadlightDirection");
|
|
loadAngles(ui->backLightHorizontalAngle, ui->backLightVerticalAngle, "BacklightDirection");
|
|
loadAngles(ui->fillLightHorizontalAngle, ui->fillLightVerticalAngle, "FillLightDirection");
|
|
}
|
|
|
|
void DlgSettingsLightSources::resetSettingsToDefaults()
|
|
{
|
|
PreferencePage::resetSettingsToDefaults();
|
|
|
|
hGrp->RemoveASCII("HeadlightDirection");
|
|
hGrp->RemoveASCII("BacklightDirection");
|
|
hGrp->RemoveASCII("FillLightDirection");
|
|
|
|
loadSettings();
|
|
configureViewer();
|
|
}
|
|
|
|
void DlgSettingsLightSources::zoomIn() const
|
|
{
|
|
if (camera == nullptr) {
|
|
return;
|
|
}
|
|
|
|
camera->height = camera->height.getValue() - zoomStep;
|
|
}
|
|
|
|
void DlgSettingsLightSources::zoomOut() const
|
|
{
|
|
if (camera == nullptr) {
|
|
return;
|
|
}
|
|
|
|
camera->height = camera->height.getValue() + zoomStep;
|
|
}
|
|
|
|
void DlgSettingsLightSources::changeEvent(QEvent* event)
|
|
{
|
|
if (event->type() == QEvent::LanguageChange) {
|
|
ui->retranslateUi(this);
|
|
}
|
|
PreferencePage::changeEvent(event);
|
|
}
|
|
|
|
#include "moc_DlgSettingsLightSources.cpp"
|