From 86bf176b1fd058d883be49dc3628042cfc27be2a Mon Sep 17 00:00:00 2001 From: wmayer Date: Sun, 23 Oct 2022 19:49:46 +0200 Subject: [PATCH] Mesh: implement PropertyMaterial --- src/Mod/Mesh/App/AppMesh.cpp | 1 + src/Mod/Mesh/App/Core/MeshIO.cpp | 26 +++ src/Mod/Mesh/App/Core/MeshIO.h | 3 + src/Mod/Mesh/App/MeshProperties.cpp | 318 ++++++++++++++++++++++++++++ src/Mod/Mesh/App/MeshProperties.h | 56 ++++- src/Mod/Mesh/App/MeshTestsApp.py | 42 ++++ 6 files changed, 444 insertions(+), 2 deletions(-) diff --git a/src/Mod/Mesh/App/AppMesh.cpp b/src/Mod/Mesh/App/AppMesh.cpp index 9e8b13932e..8bd25946d3 100644 --- a/src/Mod/Mesh/App/AppMesh.cpp +++ b/src/Mod/Mesh/App/AppMesh.cpp @@ -74,6 +74,7 @@ PyMOD_INIT_FUNC(Mesh) // init Type system Mesh::PropertyNormalList ::init(); Mesh::PropertyCurvatureList ::init(); + Mesh::PropertyMaterial ::init(); Mesh::PropertyMeshKernel ::init(); Mesh::MeshObject ::init(); diff --git a/src/Mod/Mesh/App/Core/MeshIO.cpp b/src/Mod/Mesh/App/Core/MeshIO.cpp index dd2c200b92..cc932dd637 100644 --- a/src/Mod/Mesh/App/Core/MeshIO.cpp +++ b/src/Mod/Mesh/App/Core/MeshIO.cpp @@ -99,6 +99,32 @@ struct QUAD {int iV[4];}; // -------------------------------------------------------------- +bool Material::operator == (const Material& mat) const +{ + if (binding != mat.binding) + return false; + if (ambientColor != mat.ambientColor) + return false; + if (diffuseColor != mat.diffuseColor) + return false; + if (specularColor != mat.specularColor) + return false; + if (emissiveColor != mat.emissiveColor) + return false; + if (shininess != mat.shininess) + return false; + if (transparency != mat.transparency) + return false; + return true; +} + +bool Material::operator != (const Material& mat) const +{ + return !operator==(mat); +} + +// -------------------------------------------------------------- + std::vector MeshInput::supportedMeshFormats() { std::vector fmt; diff --git a/src/Mod/Mesh/App/Core/MeshIO.h b/src/Mod/Mesh/App/Core/MeshIO.h index 58315da313..b2edf2ddef 100644 --- a/src/Mod/Mesh/App/Core/MeshIO.h +++ b/src/Mod/Mesh/App/Core/MeshIO.h @@ -83,6 +83,9 @@ struct MeshExport Material std::vector emissiveColor; /**< Defines the emissive color. */ std::vector shininess; std::vector transparency; + + bool operator == (const Material& mat) const; + bool operator != (const Material& mat) const; }; struct MeshExport Group diff --git a/src/Mod/Mesh/App/MeshProperties.cpp b/src/Mod/Mesh/App/MeshProperties.cpp index e11b8803b0..a35a345800 100644 --- a/src/Mod/Mesh/App/MeshProperties.cpp +++ b/src/Mod/Mesh/App/MeshProperties.cpp @@ -46,6 +46,7 @@ using namespace Mesh; TYPESYSTEM_SOURCE(Mesh::PropertyNormalList, App::PropertyLists) TYPESYSTEM_SOURCE(Mesh::PropertyCurvatureList , App::PropertyLists) +TYPESYSTEM_SOURCE(Mesh::PropertyMaterial , App::Property) TYPESYSTEM_SOURCE(Mesh::PropertyMeshKernel , App::PropertyComplexGeoData) PropertyNormalList::PropertyNormalList() @@ -426,6 +427,323 @@ void PropertyCurvatureList::Paste(const App::Property &from) // ---------------------------------------------------------------------------- +const MeshCore::Material& PropertyMaterial::getValue() const +{ + return _material; +} + +MeshCore::MeshIO::Binding PropertyMaterial::getBinding() const +{ + return _material.binding; +} + +const std::vector& PropertyMaterial::getAmbientColor() const +{ + return _material.ambientColor; +} + +const std::vector& PropertyMaterial::getDiffuseColor() const +{ + return _material.diffuseColor; +} + +const std::vector& PropertyMaterial::getSpecularColor() const +{ + return _material.specularColor; +} + +const std::vector& PropertyMaterial::getEmissiveColor() const +{ + return _material.emissiveColor; +} + +const std::vector& PropertyMaterial::getShininess() const +{ + return _material.shininess; +} + +const std::vector& PropertyMaterial::getTransparency() const +{ + return _material.transparency; +} + +void PropertyMaterial::setValue(const MeshCore::Material& value) +{ + aboutToSetValue(); + _material = value; + hasSetValue(); +} + +void PropertyMaterial::setAmbientColor(const std::vector& value) +{ + aboutToSetValue(); + _material.ambientColor = value; + hasSetValue(); +} + +void PropertyMaterial::setDiffuseColor(const std::vector& value) +{ + aboutToSetValue(); + _material.diffuseColor = value; + hasSetValue(); +} + +void PropertyMaterial::setSpecularColor(const std::vector& value) +{ + aboutToSetValue(); + _material.specularColor = value; + hasSetValue(); +} + +void PropertyMaterial::setEmissiveColor(const std::vector& value) +{ + aboutToSetValue(); + _material.emissiveColor = value; + hasSetValue(); +} + +void PropertyMaterial::setShininess(const std::vector& value) +{ + aboutToSetValue(); + _material.shininess = value; + hasSetValue(); +} + +void PropertyMaterial::setTransparency(const std::vector& value) +{ + aboutToSetValue(); + _material.transparency = value; + hasSetValue(); +} + +void PropertyMaterial::setBinding(MeshCore::MeshIO::Binding bind) +{ + aboutToSetValue(); + _material.binding = bind; + hasSetValue(); +} + +PyObject* PropertyMaterial::getPyObject() +{ + auto getColorList = [](const std::vector& color) { + Py::List list; + for (const auto& it : color) { + list.append(Py::TupleN(Py::Float(it.r), + Py::Float(it.g), + Py::Float(it.b))); + } + return list; + }; + + auto getFloatList = [](const std::vector& value) { + Py::List list; + for (auto it : value) { + list.append(Py::Float(it)); + } + return list; + }; + + Py::Dict dict; + dict.setItem("binding", Py::Long(static_cast(_material.binding))); + dict.setItem("ambientColor", getColorList(_material.ambientColor)); + dict.setItem("diffuseColor", getColorList(_material.diffuseColor)); + dict.setItem("specularColor", getColorList(_material.specularColor)); + dict.setItem("emissiveColor", getColorList(_material.emissiveColor)); + dict.setItem("shininess", getFloatList(_material.shininess)); + dict.setItem("transparency", getFloatList(_material.transparency)); + + return Py::new_reference_to(dict); +} + +void PropertyMaterial::setPyObject(PyObject* obj) +{ + auto getColorList = [](const Py::Dict& dict, const std::string& key) { + std::vector color; + if (dict.hasKey(key)) { + Py::Sequence list(dict.getItem(key)); + color.reserve(list.size()); + for (const auto& it : list) { + Py::Sequence tuple(it); + float r = static_cast(Py::Float(tuple[0])); + float g = static_cast(Py::Float(tuple[1])); + float b = static_cast(Py::Float(tuple[2])); + color.emplace_back(r, g, b); + } + } + return color; + }; + + auto getFloatList = [](const Py::Dict& dict, const std::string& key) { + std::vector value; + if (dict.hasKey(key)) { + Py::Sequence list(dict.getItem(key)); + value.reserve(list.size()); + for (const auto& it : list) { + value.push_back(static_cast(Py::Float(it))); + } + } + return value; + }; + + try { + MeshCore::Material material; + Py::Dict dict(obj); + + if (dict.hasKey("binding")) { + Py::Long binding(dict.getItem("binding")); + int bind = static_cast(binding); + material.binding = static_cast(bind); + } + + material.ambientColor = getColorList(dict, "ambientColor"); + material.diffuseColor = getColorList(dict, "diffuseColor"); + material.specularColor = getColorList(dict, "specularColor"); + material.emissiveColor = getColorList(dict, "emissiveColor"); + material.shininess = getFloatList(dict, "shininess"); + material.transparency = getFloatList(dict, "transparency"); + + setValue(material); + } + catch (Py::Exception& e) { + e.clear(); + throw Base::TypeError("Not a dict with expected keys"); + } +} + +void PropertyMaterial::Save(Base::Writer& writer) const +{ + if (!writer.isForceXML()) { + writer.Stream() << writer.ind() << "" << std::endl; + } +} + +void PropertyMaterial::Restore(Base::XMLReader& reader) +{ + reader.readElement("Material"); + if (reader.hasAttribute("file")) { + std::string file(reader.getAttribute("file")); + + if (!file.empty()) { + // initiate a file read + reader.addFile(file.c_str(), this); + } + } +} + +void PropertyMaterial::SaveDocFile(Base::Writer &writer) const +{ + Base::OutputStream str(writer.Stream()); + auto saveColor = [&str](const std::vector& color) { + uint32_t count = static_cast(color.size()); + str << count; + for (const auto& it : color) { + str << it.getPackedValue(); + } + }; + + auto saveFloat = [&str](const std::vector& value) { + uint32_t count = static_cast(value.size()); + str << count; + for (const auto& it : value) { + str << it; + } + }; + + uint32_t bind = static_cast(_material.binding); + str << bind; + + saveColor(_material.ambientColor); + saveColor(_material.diffuseColor); + saveColor(_material.specularColor); + saveColor(_material.emissiveColor); + saveFloat(_material.shininess); + saveFloat(_material.transparency); +} + +void PropertyMaterial::RestoreDocFile(Base::Reader &reader) +{ + Base::InputStream str(reader); + auto restoreColor = [&str](std::vector& color) { + uint32_t count = 0; + str >> count; + color.resize(count); + for (auto& it : color) { + uint32_t value; // must be 32 bit long + str >> value; + it.setPackedValue(value); + } + }; + + auto restoreFloat = [&str](std::vector& value) { + uint32_t count = 0; + str >> count; + value.resize(count); + for (auto& it : value) { + float valueF; + str >> valueF; + it = valueF; + } + }; + + MeshCore::Material material; + + uint32_t bind = 0; + str >> bind; + material.binding = static_cast(bind); + + restoreColor(material.ambientColor); + restoreColor(material.diffuseColor); + restoreColor(material.specularColor); + restoreColor(material.emissiveColor); + restoreFloat(material.shininess); + restoreFloat(material.transparency); + + setValue(material); +} + +const char* PropertyMaterial::getEditorName() const +{ + return ""; +} + +App::Property* PropertyMaterial::Copy() const +{ + PropertyMaterial *prop = new PropertyMaterial(); + prop->_material = _material; + return prop; +} + +void PropertyMaterial::Paste(const Property& from) +{ + aboutToSetValue(); + using ObjectType = std::remove_pointer::type; + _material = dynamic_cast(from)._material; + hasSetValue(); +} + +unsigned int PropertyMaterial::getMemSize() const +{ + auto size = _material.ambientColor.size() + + _material.diffuseColor.size() + + _material.emissiveColor.size() + + _material.shininess.size() + + _material.specularColor.size() + + _material.transparency.size() + + _material.library.size() + sizeof(_material); + return static_cast(size); +} + +bool PropertyMaterial::isSame(const App::Property& other) const +{ + if (&other == this) + return true; + return getTypeId() == other.getTypeId() + && getValue() == static_cast(&other)->getValue(); +} + +// ---------------------------------------------------------------------------- + PropertyMeshKernel::PropertyMeshKernel() : _meshObject(new MeshObject()), meshPyObject(nullptr) { diff --git a/src/Mod/Mesh/App/MeshProperties.h b/src/Mod/Mesh/App/MeshProperties.h index 46f9e997ad..eeff7c1e17 100644 --- a/src/Mod/Mesh/App/MeshProperties.h +++ b/src/Mod/Mesh/App/MeshProperties.h @@ -37,8 +37,9 @@ #include #include -#include "Core/MeshKernel.h" -#include "Mesh.h" +#include +#include +#include namespace Mesh @@ -164,6 +165,57 @@ private: std::vector _lValueList; }; +/** Mesh material properties + */ +class MeshExport PropertyMaterial : public App::Property +{ + TYPESYSTEM_HEADER_WITH_OVERRIDE(); + +public: + PropertyMaterial() = default; + ~PropertyMaterial() override = default; + + /** Sets the property + */ + void setValue(const MeshCore::Material &mat); + void setAmbientColor(const std::vector& col); + void setDiffuseColor(const std::vector& col); + void setSpecularColor(const std::vector& col); + void setEmissiveColor(const std::vector& col); + void setShininess(const std::vector&); + void setTransparency(const std::vector&); + void setBinding(MeshCore::MeshIO::Binding); + + const MeshCore::Material& getValue() const; + const std::vector& getAmbientColor() const; + const std::vector& getDiffuseColor() const; + const std::vector& getSpecularColor() const; + const std::vector& getEmissiveColor() const; + const std::vector& getShininess() const; + const std::vector& getTransparency() const; + MeshCore::MeshIO::Binding getBinding() const; + + PyObject* getPyObject() override; + void setPyObject(PyObject*) override; + + void Save (Base::Writer& writer) const override; + void Restore(Base::XMLReader& reader) override; + + void SaveDocFile(Base::Writer& writer) const override; + void RestoreDocFile(Base::Reader& reader) override; + + const char* getEditorName() const override; + + Property* Copy() const override; + void Paste(const Property& from) override; + + unsigned int getMemSize() const override; + bool isSame(const Property& other) const override; + +private: + MeshCore::Material _material; +}; + /** The mesh kernel property class. * @author Werner Mayer */ diff --git a/src/Mod/Mesh/App/MeshTestsApp.py b/src/Mod/Mesh/App/MeshTestsApp.py index 23e286a993..57617d3298 100644 --- a/src/Mod/Mesh/App/MeshTestsApp.py +++ b/src/Mod/Mesh/App/MeshTestsApp.py @@ -8,6 +8,7 @@ import os import sys import io import FreeCAD, unittest, Mesh +import MeshEnums from FreeCAD import Base import time, tempfile, math # http://python-kurs.eu/threads.php @@ -681,3 +682,44 @@ class MeshSubElement(unittest.TestCase): def tearDown(self): pass + +class MeshProperty(unittest.TestCase): + def setUp(self): + self.doc = FreeCAD.newDocument("MeshTest") + + def tearDown(self): + FreeCAD.closeDocument(self.doc.Name) + + def testMaterial(self): + mesh = self.doc.addObject("Mesh::Feature", "Sphere") + mesh.Mesh = Mesh.createBox(1.0, 1.0, 1.0) + len1 = int(mesh.Mesh.CountFacets / 2) + len2 = int(mesh.Mesh.CountFacets - len1) + material = {"transparency" : [0.2] * len1 + [0.8] * len2} + material["binding"] = MeshEnums.Binding.PER_FACE + material["ambientColor"] = [(1,0,0)] * (len1 + len2) + material["diffuseColor"] = [(0,1,0)] * (len1 + len2) + material["specularColor"] = [(0,0,1)] * (len1 + len2) + material["emissiveColor"] = [(1,1,1)] * (len1 + len2) + material["shininess"] = [0.3] * (len1 + len2) + + mesh.addProperty("Mesh::PropertyMaterial", "Material") + mesh.Material = material + + TempPath = tempfile.gettempdir() + SaveName = TempPath + os.sep + "mesh_with_material.FCStd" + self.doc.saveAs(SaveName) + FreeCAD.closeDocument(self.doc.Name) + + self.doc = FreeCAD.openDocument(SaveName) + mesh2 = self.doc.Sphere + material2 = mesh2.Material + + self.assertEqual(int(material2["binding"]), int(MeshEnums.Binding.PER_FACE)) + self.assertEqual(len(material2["ambientColor"]), len1 + len2) + self.assertEqual(len(material2["diffuseColor"]), len1 + len2) + self.assertEqual(len(material2["specularColor"]), len1 + len2) + self.assertEqual(len(material2["emissiveColor"]), len1 + len2) + self.assertEqual(len(material2["shininess"]), len1 + len2) + self.assertEqual(len(material2["transparency"]), len1 + len2) +