FreeCAD

Форк
0
/
MaterialManager.cpp 
410 строк · 12.6 Кб
1
/***************************************************************************
2
 *   Copyright (c) 2023 David Carter <dcarter@david.carter.ca>             *
3
 *                                                                         *
4
 *   This file is part of FreeCAD.                                         *
5
 *                                                                         *
6
 *   FreeCAD is free software: you can redistribute it and/or modify it    *
7
 *   under the terms of the GNU Lesser General Public License as           *
8
 *   published by the Free Software Foundation, either version 2.1 of the  *
9
 *   License, or (at your option) any later version.                       *
10
 *                                                                         *
11
 *   FreeCAD is distributed in the hope that it will be useful, but        *
12
 *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
13
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU      *
14
 *   Lesser General Public License for more details.                       *
15
 *                                                                         *
16
 *   You should have received a copy of the GNU Lesser General Public      *
17
 *   License along with FreeCAD. If not, see                               *
18
 *   <https://www.gnu.org/licenses/>.                                      *
19
 *                                                                         *
20
 **************************************************************************/
21

22
#include "PreCompiled.h"
23
#ifndef _PreComp_
24
#include <random>
25
#endif
26

27
#include <QMutex>
28
#include <QDirIterator>
29
#include <QMutexLocker>
30

31
#include <App/Application.h>
32
#include <App/Material.h>
33

34
#include "Exceptions.h"
35
#include "MaterialConfigLoader.h"
36
#include "MaterialLoader.h"
37
#include "MaterialManager.h"
38
#include "ModelManager.h"
39
#include "ModelUuids.h"
40

41

42
using namespace Materials;
43

44
/* TRANSLATOR Material::Materials */
45

46
std::shared_ptr<std::list<std::shared_ptr<MaterialLibrary>>> MaterialManager::_libraryList =
47
    nullptr;
48
std::shared_ptr<std::map<QString, std::shared_ptr<Material>>> MaterialManager::_materialMap =
49
    nullptr;
50
QMutex MaterialManager::_mutex;
51

52
TYPESYSTEM_SOURCE(Materials::MaterialManager, Base::BaseClass)
53

54
MaterialManager::MaterialManager()
55
{
56
    // TODO: Add a mutex or similar
57
    initLibraries();
58
}
59

60
void MaterialManager::initLibraries()
61
{
62
    QMutexLocker locker(&_mutex);
63

64
    if (_materialMap == nullptr) {
65
        // Load the models first
66
        auto manager = std::make_unique<ModelManager>();
67
        Q_UNUSED(manager)
68

69
        _materialMap = std::make_shared<std::map<QString, std::shared_ptr<Material>>>();
70

71
        if (_libraryList == nullptr) {
72
            _libraryList = std::make_shared<std::list<std::shared_ptr<MaterialLibrary>>>();
73
        }
74

75
        // Load the libraries
76
        MaterialLoader loader(_materialMap, _libraryList);
77
    }
78
}
79

80
void MaterialManager::cleanup()
81
{
82
    QMutexLocker locker(&_mutex);
83

84
    if (_libraryList) {
85
        _libraryList->clear();
86
        _libraryList = nullptr;
87
    }
88

89
    if (_materialMap) {
90
        for (auto& it : *_materialMap) {
91
            // This is needed to resolve cyclic dependencies
92
            it.second->setLibrary(nullptr);
93
        }
94
        _materialMap->clear();
95
        _materialMap = nullptr;
96
    }
97
}
98

99
void MaterialManager::refresh()
100
{
101
    // This is very expensive and can be improved using observers?
102
    cleanup();
103
    initLibraries();
104
}
105

106
void MaterialManager::saveMaterial(const std::shared_ptr<MaterialLibrary>& library,
107
                                   const std::shared_ptr<Material>& material,
108
                                   const QString& path,
109
                                   bool overwrite,
110
                                   bool saveAsCopy,
111
                                   bool saveInherited) const
112
{
113
    auto newMaterial = library->saveMaterial(material, path, overwrite, saveAsCopy, saveInherited);
114
    (*_materialMap)[newMaterial->getUUID()] = newMaterial;
115
}
116

117
bool MaterialManager::isMaterial(const fs::path& p) const
118
{
119
    if (!fs::is_regular_file(p)) {
120
        return false;
121
    }
122
    // check file extension
123
    if (p.extension() == ".FCMat") {
124
        return true;
125
    }
126
    return false;
127
}
128

129
bool MaterialManager::isMaterial(const QFileInfo& file) const
130
{
131
    if (!file.isFile()) {
132
        return false;
133
    }
134
    // check file extension
135
    if (file.suffix() == QString::fromStdString("FCMat")) {
136
        return true;
137
    }
138
    return false;
139
}
140

141
std::shared_ptr<App::Material> MaterialManager::defaultAppearance()
142
{
143
    ParameterGrp::handle hGrp =
144
        App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/View");
145

146
    auto getColor = [hGrp](const char* parameter, App::Color& color) {
147
        uint32_t packed = color.getPackedRGB();
148
        packed = hGrp->GetUnsigned(parameter, packed);
149
        color.setPackedRGB(packed);
150
        color.a = 1.0; // The default color sets fully transparent, not opaque
151
    };
152
    auto intRandom = [](int min, int max) -> int {
153
        static std::mt19937 generator;
154
        std::uniform_int_distribution<int> distribution(min, max);
155
        return distribution(generator);
156
    };
157

158
    App::Material mat(App::Material::DEFAULT);
159
    bool randomColor = hGrp->GetBool("RandomColor", false);
160

161
    if (randomColor) {
162
        float red = static_cast<float>(intRandom(0, 255)) / 255.0F;
163
        float green = static_cast<float>(intRandom(0, 255)) / 255.0F;
164
        float blue = static_cast<float>(intRandom(0, 255)) / 255.0F;
165
        mat.diffuseColor = App::Color(red, green, blue, 1.0);
166
    }
167
    else {
168
        getColor("DefaultShapeColor", mat.diffuseColor);
169
    }
170

171
    getColor("DefaultAmbientColor", mat.ambientColor);
172
    getColor("DefaultEmissiveColor", mat.emissiveColor);
173
    getColor("DefaultSpecularColor", mat.specularColor);
174

175
    long initialTransparency = hGrp->GetInt("DefaultShapeTransparency", 0);
176
    long initialShininess = hGrp->GetInt("DefaultShapeShininess", 90);
177
    mat.shininess = ((float)initialShininess / 100.0F);
178
    mat.transparency = ((float)initialTransparency / 100.0F);
179

180
    return std::make_shared<App::Material>(mat);
181
}
182

183
std::shared_ptr<Material> MaterialManager::defaultMaterial()
184
{
185
    MaterialManager manager;
186

187
    auto mat = defaultAppearance();
188
    auto material = manager.getMaterial(defaultMaterialUUID());
189
    if (!material) {
190
        material = manager.getMaterial(QLatin1String("7f9fd73b-50c9-41d8-b7b2-575a030c1eeb"));
191
    }
192
    if (material->hasAppearanceModel(ModelUUIDs::ModelUUID_Rendering_Basic)) {
193
        material->getAppearanceProperty(QString::fromLatin1("DiffuseColor"))
194
            ->setColor(mat->diffuseColor);
195
        material->getAppearanceProperty(QString::fromLatin1("AmbientColor"))
196
            ->setColor(mat->ambientColor);
197
        material->getAppearanceProperty(QString::fromLatin1("EmissiveColor"))
198
            ->setColor(mat->emissiveColor);
199
        material->getAppearanceProperty(QString::fromLatin1("SpecularColor"))
200
            ->setColor(mat->specularColor);
201
        material->getAppearanceProperty(QString::fromLatin1("Transparency"))
202
            ->setFloat(mat->transparency);
203
        material->getAppearanceProperty(QString::fromLatin1("Shininess"))
204
            ->setFloat(mat->shininess);
205
    }
206

207
    return material;
208
}
209

210
QString MaterialManager::defaultMaterialUUID()
211
{
212
    // Make this a preference
213
    auto param = App::GetApplication().GetParameterGroupByPath(
214
        "User parameter:BaseApp/Preferences/Mod/Material");
215
    auto uuid = param->GetASCII("DefaultMaterial", "7f9fd73b-50c9-41d8-b7b2-575a030c1eeb");
216
    return QString::fromStdString(uuid);
217
}
218

219
std::shared_ptr<Material> MaterialManager::getMaterial(const QString& uuid) const
220
{
221
    try {
222
        return _materialMap->at(uuid);
223
    }
224
    catch (std::out_of_range&) {
225
        throw MaterialNotFound();
226
    }
227
}
228

229
std::shared_ptr<Material> MaterialManager::getMaterial(const App::Material& material)
230
{
231
    MaterialManager manager;
232

233
    return manager.getMaterial(QString::fromStdString(material.uuid));
234
}
235

236
std::shared_ptr<Material> MaterialManager::getMaterialByPath(const QString& path) const
237
{
238
    QString cleanPath = QDir::cleanPath(path);
239

240
    for (auto& library : *_libraryList) {
241
        if (cleanPath.startsWith(library->getDirectory())) {
242
            try {
243
                return library->getMaterialByPath(cleanPath);
244
            }
245
            catch (const MaterialNotFound&) {
246
            }
247

248
            // See if it's a new file saved by the old editor
249
            {
250
                QMutexLocker locker(&_mutex);
251

252
                if (MaterialConfigLoader::isConfigStyle(path)) {
253
                    auto material = MaterialConfigLoader::getMaterialFromPath(library, path);
254
                    if (material) {
255
                        (*_materialMap)[material->getUUID()] = library->addMaterial(material, path);
256
                    }
257

258
                    return material;
259
                }
260
            }
261
        }
262
    }
263

264
    // Older workbenches may try files outside the context of a library
265
    {
266
        QMutexLocker locker(&_mutex);
267

268
        if (MaterialConfigLoader::isConfigStyle(path)) {
269
            auto material = MaterialConfigLoader::getMaterialFromPath(nullptr, path);
270

271
            return material;
272
        }
273
    }
274

275
    throw MaterialNotFound();
276
}
277

278
std::shared_ptr<Material> MaterialManager::getMaterialByPath(const QString& path,
279
                                                             const QString& lib) const
280
{
281
    auto library = getLibrary(lib);           // May throw LibraryNotFound
282
    return library->getMaterialByPath(path);  // May throw MaterialNotFound
283
}
284

285
bool MaterialManager::exists(const QString& uuid) const
286
{
287
    try {
288
        auto material = getMaterial(uuid);
289
        if (material) {
290
            return true;
291
        }
292
    }
293
    catch (const MaterialNotFound&) {
294
    }
295

296
    return false;
297
}
298

299
std::shared_ptr<Material>
300
MaterialManager::getParent(const std::shared_ptr<Material>& material) const
301
{
302
    if (material->getParentUUID().isEmpty()) {
303
        throw MaterialNotFound();
304
    }
305

306
    return getMaterial(material->getParentUUID());
307
}
308

309
bool MaterialManager::exists(const std::shared_ptr<MaterialLibrary>& library,
310
                             const QString& uuid) const
311
{
312
    try {
313
        auto material = getMaterial(uuid);
314
        if (material) {
315
            return (*material->getLibrary() == *library);
316
        }
317
    }
318
    catch (const MaterialNotFound&) {
319
    }
320

321
    return false;
322
}
323

324
std::shared_ptr<MaterialLibrary> MaterialManager::getLibrary(const QString& name) const
325
{
326
    for (auto& library : *_libraryList) {
327
        if (library->getName() == name) {
328
            return library;
329
        }
330
    }
331

332
    throw LibraryNotFound();
333
}
334

335
std::shared_ptr<std::list<std::shared_ptr<MaterialLibrary>>>
336
MaterialManager::getMaterialLibraries() const
337
{
338
    if (_libraryList == nullptr) {
339
        if (_materialMap == nullptr) {
340
            _materialMap = std::make_shared<std::map<QString, std::shared_ptr<Material>>>();
341
        }
342
        _libraryList = std::make_shared<std::list<std::shared_ptr<MaterialLibrary>>>();
343

344
        // Load the libraries
345
        MaterialLoader loader(_materialMap, _libraryList);
346
    }
347
    return _libraryList;
348
}
349

350
std::shared_ptr<std::list<QString>>
351
MaterialManager::getMaterialFolders(const std::shared_ptr<MaterialLibrary>& library) const
352
{
353
    return MaterialLoader::getMaterialFolders(*library);
354
}
355

356
std::shared_ptr<std::map<QString, std::shared_ptr<Material>>>
357
MaterialManager::materialsWithModel(const QString& uuid) const
358
{
359
    std::shared_ptr<std::map<QString, std::shared_ptr<Material>>> dict =
360
        std::make_shared<std::map<QString, std::shared_ptr<Material>>>();
361

362
    for (auto& it : *_materialMap) {
363
        QString key = it.first;
364
        auto material = it.second;
365

366
        if (material->hasModel(uuid)) {
367
            (*dict)[key] = material;
368
        }
369
    }
370

371
    return dict;
372
}
373

374
std::shared_ptr<std::map<QString, std::shared_ptr<Material>>>
375
MaterialManager::materialsWithModelComplete(const QString& uuid) const
376
{
377
    std::shared_ptr<std::map<QString, std::shared_ptr<Material>>> dict =
378
        std::make_shared<std::map<QString, std::shared_ptr<Material>>>();
379

380
    for (auto& it : *_materialMap) {
381
        QString key = it.first;
382
        auto material = it.second;
383

384
        if (material->isModelComplete(uuid)) {
385
            (*dict)[key] = material;
386
        }
387
    }
388

389
    return dict;
390
}
391

392
void MaterialManager::dereference() const
393
{
394
    // First clear the inheritences
395
    for (auto& it : *_materialMap) {
396
        auto material = it.second;
397
        material->clearDereferenced();
398
        material->clearInherited();
399
    }
400

401
    // Run the dereference again
402
    for (auto& it : *_materialMap) {
403
        dereference(it.second);
404
    }
405
}
406

407
void MaterialManager::dereference(std::shared_ptr<Material> material) const
408
{
409
    MaterialLoader::dereference(_materialMap, material);
410
}
411

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.