19 changed files with 423 additions and 14 deletions
@ -0,0 +1,42 @@ |
|||||||
|
.. |
||||||
|
This file is part of Magnum. |
||||||
|
|
||||||
|
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, |
||||||
|
2020, 2021, 2022, 2023 Vladimír Vondruš <mosra@centrum.cz> |
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a |
||||||
|
copy of this software and associated documentation files (the "Software"), |
||||||
|
to deal in the Software without restriction, including without limitation |
||||||
|
the rights to use, copy, modify, merge, publish, distribute, sublicense, |
||||||
|
and/or sell copies of the Software, and to permit persons to whom the |
||||||
|
Software is furnished to do so, subject to the following conditions: |
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included |
||||||
|
in all copies or substantial portions of the Software. |
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
||||||
|
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
||||||
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
||||||
|
DEALINGS IN THE SOFTWARE. |
||||||
|
.. |
||||||
|
|
||||||
|
.. py:function:: magnum.materialtools.filter_attributes |
||||||
|
:raise AssertionError: If size of :p:`attributes_to_keep` is different than |
||||||
|
:ref:`trade.MaterialData.attribute_data_offset()` for |
||||||
|
:ref:`trade.MaterialData.layer_count` |
||||||
|
.. py:function:: magnum.materialtools.filter_layers |
||||||
|
:raise AssertionError: If size of :p:`layers_to_keep` is different than |
||||||
|
:ref:`trade.MaterialData.layer_count` |
||||||
|
.. py:function:: magnum.materialtools.filter_attributes_layers |
||||||
|
:raise AssertionError: If size of :p:`attributes_to_keep` is different than |
||||||
|
:ref:`trade.MaterialData.attribute_data_offset()` for |
||||||
|
:ref:`trade.MaterialData.layer_count` |
||||||
|
:raise AssertionError: If size of :p:`layers_to_keep` is different than |
||||||
|
:ref:`trade.MaterialData.layer_count` |
||||||
|
.. py:function:: magnum.materialtools.merge |
||||||
|
:raise RuntimeError: If merge failed due to a conflict |
||||||
|
.. py:function:: magnum.materialtools.phong_to_pbr_metallic_roughness |
||||||
|
:raise RuntimeError: If conversion failed |
||||||
@ -0,0 +1,135 @@ |
|||||||
|
/*
|
||||||
|
This file is part of Magnum. |
||||||
|
|
||||||
|
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, |
||||||
|
2020, 2021, 2022, 2023 Vladimír Vondruš <mosra@centrum.cz> |
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a |
||||||
|
copy of this software and associated documentation files (the "Software"), |
||||||
|
to deal in the Software without restriction, including without limitation |
||||||
|
the rights to use, copy, modify, merge, publish, distribute, sublicense, |
||||||
|
and/or sell copies of the Software, and to permit persons to whom the |
||||||
|
Software is furnished to do so, subject to the following conditions: |
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included |
||||||
|
in all copies or substantial portions of the Software. |
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
||||||
|
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
||||||
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
||||||
|
DEALINGS IN THE SOFTWARE. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <pybind11/pybind11.h> |
||||||
|
#include <pybind11/stl.h> /* for std::vector */ |
||||||
|
#include <Corrade/Containers/BitArrayView.h> |
||||||
|
#include <Corrade/Containers/Iterable.h> |
||||||
|
#include <Corrade/Containers/ArrayViewStl.h> |
||||||
|
#include <Magnum/MaterialTools/Copy.h> |
||||||
|
#include <Magnum/MaterialTools/Filter.h> |
||||||
|
#include <Magnum/MaterialTools/Merge.h> |
||||||
|
#include <Magnum/MaterialTools/PhongToPbrMetallicRoughness.h> |
||||||
|
#include <Magnum/MaterialTools/RemoveDuplicates.h> |
||||||
|
#include <Magnum/Trade/MaterialData.h> |
||||||
|
|
||||||
|
#include "Magnum/Trade/PythonBindings.h" |
||||||
|
#include "corrade/EnumOperators.h" |
||||||
|
#include "magnum/bootstrap.h" |
||||||
|
|
||||||
|
namespace magnum { |
||||||
|
|
||||||
|
void materialtools(py::module_& m) { |
||||||
|
m.doc() = "Material tools"; |
||||||
|
|
||||||
|
#ifndef MAGNUM_BUILD_STATIC |
||||||
|
/* These are a part of the same module in the static build, no need to
|
||||||
|
import (also can't import because there it's _magnum.*) */ |
||||||
|
py::module_::import("magnum.trade"); |
||||||
|
#endif |
||||||
|
|
||||||
|
py::enum_<MaterialTools::MergeConflicts>{m, "MergeConflicts", "Material merge conflict resolution"} |
||||||
|
.value("FAIL", MaterialTools::MergeConflicts::Fail) |
||||||
|
.value("KEEP_FIRST_IF_SAME_TYPE", MaterialTools::MergeConflicts::KeepFirstIfSameType) |
||||||
|
.value("KEEP_FIRST_IGNORE_TYPE", MaterialTools::MergeConflicts::KeepFirstIgnoreType); |
||||||
|
|
||||||
|
py::enum_<MaterialTools::PhongToPbrMetallicRoughnessFlag> phongToPbrMetallicRoughnessFlag{m, "PhongToPbrMetallicRoughnessFlags", "Phong to PBR conversion flags"}; |
||||||
|
phongToPbrMetallicRoughnessFlag |
||||||
|
.value("KEEP_ORIGINAL_ATTRIBUTES", MaterialTools::PhongToPbrMetallicRoughnessFlag::KeepOriginalAttributes) |
||||||
|
.value("DROP_UNCOVERTIBLE_ATTRIBUTES", MaterialTools::PhongToPbrMetallicRoughnessFlag::DropUnconvertibleAttributes) |
||||||
|
.value("FAIL_ON_UNCONVERTIBLE_ATTRIBUTES", MaterialTools::PhongToPbrMetallicRoughnessFlag::FailOnUnconvertibleAttributes) |
||||||
|
.value("NONE", MaterialTools::PhongToPbrMetallicRoughnessFlag{}) |
||||||
|
.value("ALL", MaterialTools::PhongToPbrMetallicRoughnessFlag(Containers::enumCastUnderlyingType(~MaterialTools::PhongToPbrMetallicRoughnessFlags{}))); |
||||||
|
corrade::enumOperators(phongToPbrMetallicRoughnessFlag); |
||||||
|
|
||||||
|
m |
||||||
|
.def("copy", static_cast<Trade::MaterialData(*)(const Trade::MaterialData&)>(MaterialTools::copy), "Make an owned copy of the material", py::arg("material")) |
||||||
|
.def("filter_attributes", [](const Trade::MaterialData& material, const Containers::BitArrayView attributesToKeep, Trade::MaterialType typesToKeep) { |
||||||
|
if(attributesToKeep.size() != material.attributeData().size()) { |
||||||
|
PyErr_Format(PyExc_AssertionError, "expected %u bits but got %zu", material.attributeData().size(), attributesToKeep.size()); |
||||||
|
throw py::error_already_set{}; |
||||||
|
} |
||||||
|
|
||||||
|
return MaterialTools::filterAttributes(material, attributesToKeep, typesToKeep); |
||||||
|
}, "Filter material attributes", py::arg("material"), py::arg("attributes_to_keep"), py::arg("types_to_keep") = Trade::MaterialType(Containers::enumCastUnderlyingType(~Trade::MaterialTypes{}))) |
||||||
|
.def("filter_layers", [](const Trade::MaterialData& material, const Containers::BitArrayView layersToKeep, Trade::MaterialType typesToKeep) { |
||||||
|
if(layersToKeep.size() != material.layerCount()) { |
||||||
|
PyErr_Format(PyExc_AssertionError, "expected %u bits but got %zu", material.layerCount(), layersToKeep.size()); |
||||||
|
throw py::error_already_set{}; |
||||||
|
} |
||||||
|
|
||||||
|
return MaterialTools::filterLayers(material, layersToKeep, typesToKeep); |
||||||
|
}, "Filter material layers", py::arg("material"), py::arg("layers_to_keep"), py::arg("types_to_keep") = Trade::MaterialType(Containers::enumCastUnderlyingType(~Trade::MaterialTypes{}))) |
||||||
|
.def("filter_attributes_layers", [](const Trade::MaterialData& material, const Containers::BitArrayView attributesToKeep, const Containers::BitArrayView layersToKeep, Trade::MaterialType typesToKeep) { |
||||||
|
if(attributesToKeep.size() != material.attributeData().size()) { |
||||||
|
PyErr_Format(PyExc_AssertionError, "expected %u attribute bits but got %zu", material.attributeData().size(), attributesToKeep.size()); |
||||||
|
throw py::error_already_set{}; |
||||||
|
} |
||||||
|
if(layersToKeep.size() != material.layerCount()) { |
||||||
|
PyErr_Format(PyExc_AssertionError, "expected %u layer bits but got %zu", material.layerCount(), layersToKeep.size()); |
||||||
|
throw py::error_already_set{}; |
||||||
|
} |
||||||
|
|
||||||
|
return MaterialTools::filterAttributesLayers(material, attributesToKeep, layersToKeep, typesToKeep); |
||||||
|
}, "Filter material attributes and layers", py::arg("material"), py::arg("attributes_to_keep"), py::arg("layers_to_keep"), py::arg("types_to_keep") = Trade::MaterialType(Containers::enumCastUnderlyingType(~Trade::MaterialTypes{}))) |
||||||
|
.def("merge", [](const Trade::MaterialData& first, const Trade::MaterialData& second, MaterialTools::MergeConflicts conflicts) { |
||||||
|
Containers::Optional<Trade::MaterialData> out = MaterialTools::merge(first, second, conflicts); |
||||||
|
if(!out) { |
||||||
|
PyErr_SetString(PyExc_RuntimeError, "material merge failed"); |
||||||
|
throw py::error_already_set{}; |
||||||
|
} |
||||||
|
|
||||||
|
return *Utility::move(out); |
||||||
|
}, "Merge two materials", py::arg("first"), py::arg("second"), py::arg("conflicts") = MaterialTools::MergeConflicts::Fail) |
||||||
|
.def("phong_to_pbr_metallic_roughness", [](const Trade::MaterialData& material, MaterialTools::PhongToPbrMetallicRoughnessFlag flags) { |
||||||
|
Containers::Optional<Trade::MaterialData> out = MaterialTools::phongToPbrMetallicRoughness(material, flags); |
||||||
|
if(!out) { |
||||||
|
PyErr_SetString(PyExc_RuntimeError, "material conversion failed"); |
||||||
|
throw py::error_already_set{}; |
||||||
|
} |
||||||
|
|
||||||
|
return *Utility::move(out); |
||||||
|
}, "Convert a Phong material to PBR metallic/roughness", py::arg("material"), py::arg("flags") = MaterialTools::PhongToPbrMetallicRoughnessFlag{}) |
||||||
|
.def("remove_duplicates", [](std::vector<std::reference_wrapper<const Trade::MaterialData>> materials) { |
||||||
|
std::vector<UnsignedInt> indices; |
||||||
|
indices.resize(materials.size()); |
||||||
|
const std::size_t count = MaterialTools::removeDuplicatesInto(Containers::Iterable<const Trade::MaterialData>{materials.data(), materials.size(), sizeof(std::reference_wrapper<Trade::MaterialData>), [](const void* data) -> const Trade::MaterialData& { |
||||||
|
return static_cast<const std::reference_wrapper<const Trade::MaterialData>*>(data)->get(); |
||||||
|
}}, Containers::arrayView(indices)); |
||||||
|
|
||||||
|
return std::make_pair(std::move(indices), count); |
||||||
|
}, "Remove duplicate materials from a list", py::arg("materials")); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
#ifndef MAGNUM_BUILD_STATIC |
||||||
|
/* TODO: remove declaration when https://github.com/pybind/pybind11/pull/1863
|
||||||
|
is released */ |
||||||
|
extern "C" PYBIND11_EXPORT PyObject* PyInit_materialtools(); |
||||||
|
PYBIND11_MODULE(materialtools, m) { |
||||||
|
magnum::materialtools(m); |
||||||
|
} |
||||||
|
#endif |
||||||
@ -0,0 +1,188 @@ |
|||||||
|
# |
||||||
|
# This file is part of Magnum. |
||||||
|
# |
||||||
|
# Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, |
||||||
|
# 2020, 2021, 2022, 2023 Vladimír Vondruš <mosra@centrum.cz> |
||||||
|
# |
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining a |
||||||
|
# copy of this software and associated documentation files (the "Software"), |
||||||
|
# to deal in the Software without restriction, including without limitation |
||||||
|
# the rights to use, copy, modify, merge, publish, distribute, sublicense, |
||||||
|
# and/or sell copies of the Software, and to permit persons to whom the |
||||||
|
# Software is furnished to do so, subject to the following conditions: |
||||||
|
# |
||||||
|
# The above copyright notice and this permission notice shall be included |
||||||
|
# in all copies or substantial portions of the Software. |
||||||
|
# |
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||||
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
||||||
|
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||||
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
||||||
|
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
||||||
|
# DEALINGS IN THE SOFTWARE. |
||||||
|
# |
||||||
|
|
||||||
|
import os |
||||||
|
import unittest |
||||||
|
|
||||||
|
from corrade import containers |
||||||
|
from magnum import * |
||||||
|
from magnum import materialtools, trade |
||||||
|
import magnum |
||||||
|
|
||||||
|
class Copy(unittest.TestCase): |
||||||
|
def test(self): |
||||||
|
importer = trade.ImporterManager().load_and_instantiate('GltfImporter') |
||||||
|
importer.open_file(os.path.join(os.path.dirname(__file__), "material.gltf")) |
||||||
|
|
||||||
|
material = importer.material(0) |
||||||
|
self.assertEqual(material.attribute_data_flags, trade.DataFlags.OWNED|trade.DataFlags.MUTABLE) |
||||||
|
|
||||||
|
copy = materialtools.copy(material) |
||||||
|
self.assertEqual(copy.attribute_data_flags, trade.DataFlags.OWNED|trade.DataFlags.MUTABLE) |
||||||
|
|
||||||
|
class Filter(unittest.TestCase): |
||||||
|
def test(self): |
||||||
|
importer = trade.ImporterManager().load_and_instantiate('GltfImporter') |
||||||
|
# This adds extra Diffuse attributes for BaseColor and a Phong type, |
||||||
|
# don't want |
||||||
|
importer.configuration['phongMaterialFallback'] = False |
||||||
|
importer.open_file(os.path.join(os.path.dirname(__file__), "material.gltf")) |
||||||
|
|
||||||
|
material = importer.material(0) |
||||||
|
self.assertEqual(material.types, trade.MaterialTypes.PBR_METALLIC_ROUGHNESS|trade.MaterialTypes.PBR_CLEAR_COAT) |
||||||
|
self.assertEqual(material.layer_count, 2) |
||||||
|
self.assertEqual(material.attribute_count(0), 3) |
||||||
|
self.assertEqual(material.attribute_count(1), 8) |
||||||
|
|
||||||
|
# Unlike MeshData or SceneData the tools always produce a new copy of |
||||||
|
# the attribute list, so we don't need to worry about data ownership |
||||||
|
# and such |
||||||
|
|
||||||
|
attributes_to_keep = containers.BitArray.direct_init(material.attribute_data_offset(material.layer_count), True) |
||||||
|
attributes_to_keep[material.attribute_id(trade.MaterialAttribute.DOUBLE_SIDED)] = False |
||||||
|
attributes_to_keep[material.attribute_data_offset(1) + material.attribute_id(1, trade.MaterialAttribute.LAYER_FACTOR_TEXTURE_MATRIX)] = False |
||||||
|
filtered_attributes = materialtools.filter_attributes(material, attributes_to_keep, ~trade.MaterialTypes.PBR_CLEAR_COAT) |
||||||
|
self.assertEqual(filtered_attributes.types, trade.MaterialTypes.PBR_METALLIC_ROUGHNESS) |
||||||
|
self.assertEqual(filtered_attributes.layer_count, 2) |
||||||
|
self.assertEqual(filtered_attributes.attribute_count(0), 2) |
||||||
|
self.assertEqual(filtered_attributes.attribute_count(1), 7) |
||||||
|
self.assertFalse(filtered_attributes.has_attribute(trade.MaterialAttribute.DOUBLE_SIDED)) |
||||||
|
self.assertFalse(filtered_attributes.has_attribute(1, trade.MaterialAttribute.LAYER_FACTOR_TEXTURE_MATRIX)) |
||||||
|
|
||||||
|
layers_to_keep = containers.BitArray.direct_init(material.layer_count, True) |
||||||
|
layers_to_keep[1] = False |
||||||
|
filtered_layers = materialtools.filter_layers(material, layers_to_keep, ~trade.MaterialTypes.PBR_CLEAR_COAT) |
||||||
|
self.assertEqual(filtered_layers.types, trade.MaterialTypes.PBR_METALLIC_ROUGHNESS) |
||||||
|
self.assertEqual(filtered_layers.layer_count, 1) |
||||||
|
self.assertEqual(filtered_layers.attribute_count(), 3) |
||||||
|
|
||||||
|
filtered_attributes_layers = materialtools.filter_attributes_layers(material, attributes_to_keep, layers_to_keep, ~trade.MaterialTypes.PBR_CLEAR_COAT) |
||||||
|
self.assertEqual(filtered_attributes_layers.types, trade.MaterialTypes.PBR_METALLIC_ROUGHNESS) |
||||||
|
self.assertEqual(filtered_attributes_layers.layer_count, 1) |
||||||
|
self.assertEqual(filtered_attributes_layers.attribute_count(), 2) |
||||||
|
self.assertFalse(filtered_attributes_layers.has_attribute(trade.MaterialAttribute.DOUBLE_SIDED)) |
||||||
|
|
||||||
|
def test_invalid_size(self): |
||||||
|
importer = trade.ImporterManager().load_and_instantiate('GltfImporter') |
||||||
|
# This adds extra Diffuse attributes for BaseColor and a Phong type, |
||||||
|
# don't want |
||||||
|
importer.configuration['phongMaterialFallback'] = False |
||||||
|
importer.open_file(os.path.join(os.path.dirname(__file__), "material.gltf")) |
||||||
|
|
||||||
|
material = importer.material(0) |
||||||
|
self.assertEqual(material.types, trade.MaterialTypes.PBR_METALLIC_ROUGHNESS|trade.MaterialTypes.PBR_CLEAR_COAT) |
||||||
|
self.assertEqual(material.layer_count, 2) |
||||||
|
self.assertEqual(material.attribute_count(0), 3) |
||||||
|
self.assertEqual(material.attribute_count(1), 8) |
||||||
|
|
||||||
|
with self.assertRaisesRegex(AssertionError, "expected 11 bits but got 12"): |
||||||
|
materialtools.filter_attributes(material, containers.BitArray.value_init(12)) |
||||||
|
with self.assertRaisesRegex(AssertionError, "expected 2 bits but got 3"): |
||||||
|
materialtools.filter_layers(material, containers.BitArray.value_init(3)) |
||||||
|
with self.assertRaisesRegex(AssertionError, "expected 11 attribute bits but got 12"): |
||||||
|
materialtools.filter_attributes_layers(material, containers.BitArray.value_init(12), containers.BitArray.value_init(2)) |
||||||
|
with self.assertRaisesRegex(AssertionError, "expected 2 layer bits but got 3"): |
||||||
|
materialtools.filter_attributes_layers(material, containers.BitArray.value_init(11), containers.BitArray.value_init(3)) |
||||||
|
|
||||||
|
class Merge(unittest.TestCase): |
||||||
|
def test(self): |
||||||
|
importer = trade.ImporterManager().load_and_instantiate('GltfImporter') |
||||||
|
# This adds extra Diffuse attributes for BaseColor and a Phong type, |
||||||
|
# don't want |
||||||
|
importer.configuration['phongMaterialFallback'] = False |
||||||
|
importer.open_file(os.path.join(os.path.dirname(__file__), "material.gltf")) |
||||||
|
|
||||||
|
a = importer.material('A material with a layer') |
||||||
|
self.assertEqual(a.types, trade.MaterialTypes.PBR_METALLIC_ROUGHNESS|trade.MaterialTypes.PBR_CLEAR_COAT) |
||||||
|
self.assertEqual(a.layer_count, 2) |
||||||
|
self.assertEqual(a.attribute_count(0), 3) |
||||||
|
self.assertEqual(a.attribute_count(1), 8) |
||||||
|
|
||||||
|
b = importer.material('Material with an empty layer') |
||||||
|
self.assertEqual(b.types, trade.MaterialTypes.PBR_CLEAR_COAT) |
||||||
|
self.assertEqual(b.layer_count, 2) |
||||||
|
self.assertEqual(b.attribute_count(0), 1) |
||||||
|
self.assertEqual(b.attribute_count(1), 3) |
||||||
|
|
||||||
|
merged = materialtools.merge(a, b, materialtools.MergeConflicts.KEEP_FIRST_IF_SAME_TYPE) |
||||||
|
self.assertEqual(merged.types, trade.MaterialTypes.PBR_METALLIC_ROUGHNESS|trade.MaterialTypes.PBR_CLEAR_COAT) |
||||||
|
self.assertEqual(merged.layer_count, 2) |
||||||
|
self.assertEqual(merged.attribute_count(0), 4) |
||||||
|
self.assertEqual(merged.attribute_count(1), 8) |
||||||
|
|
||||||
|
def test_failed(self): |
||||||
|
importer = trade.ImporterManager().load_and_instantiate('GltfImporter') |
||||||
|
importer.open_file(os.path.join(os.path.dirname(__file__), "material.gltf")) |
||||||
|
|
||||||
|
a = importer.material('A material with a layer') |
||||||
|
b = importer.material('Material with an empty layer') |
||||||
|
|
||||||
|
with self.assertRaisesRegex(RuntimeError, "material merge failed"): |
||||||
|
materialtools.merge(a, b) |
||||||
|
|
||||||
|
class PhongToPbrMetallicRoughness(unittest.TestCase): |
||||||
|
def test(self): |
||||||
|
importer = trade.ImporterManager().load_and_instantiate('GltfImporter') |
||||||
|
# We actually *do* want the Phong stuff |
||||||
|
importer.configuration['phongMaterialFallback'] = True |
||||||
|
importer.open_file(os.path.join(os.path.dirname(__file__), "material.gltf")) |
||||||
|
material = importer.material(0) |
||||||
|
|
||||||
|
# Keep just the diffuse color, drop everything else |
||||||
|
attributes_to_keep = containers.BitArray.value_init(material.attribute_data_offset(material.layer_count)) |
||||||
|
attributes_to_keep[material.attribute_id(trade.MaterialAttribute.DIFFUSE_COLOR)] = True |
||||||
|
layers_to_keep = containers.BitArray.value_init(material.layer_count) |
||||||
|
layers_to_keep[0] = True |
||||||
|
filtered = materialtools.filter_attributes_layers(material, attributes_to_keep, layers_to_keep, ~(trade.MaterialTypes.PBR_METALLIC_ROUGHNESS|trade.MaterialTypes.PBR_CLEAR_COAT)) |
||||||
|
self.assertEqual(filtered.types, trade.MaterialTypes.PHONG) |
||||||
|
self.assertEqual(filtered.layer_count, 1) |
||||||
|
self.assertEqual(filtered.attribute_count(), 1) |
||||||
|
self.assertTrue(filtered.has_attribute(trade.MaterialAttribute.DIFFUSE_COLOR)) |
||||||
|
|
||||||
|
pbr = materialtools.phong_to_pbr_metallic_roughness(filtered) |
||||||
|
self.assertEqual(pbr.types, trade.MaterialTypes.PBR_METALLIC_ROUGHNESS) |
||||||
|
self.assertEqual(pbr.layer_count, 1) |
||||||
|
self.assertEqual(pbr.attribute_count(), 1) |
||||||
|
self.assertTrue(pbr.has_attribute(trade.MaterialAttribute.BASE_COLOR)) |
||||||
|
|
||||||
|
def test_failed(self): |
||||||
|
importer = trade.ImporterManager().load_and_instantiate('GltfImporter') |
||||||
|
importer.open_file(os.path.join(os.path.dirname(__file__), "material.gltf")) |
||||||
|
material = importer.material('Specular/glossiness') |
||||||
|
|
||||||
|
with self.assertRaisesRegex(RuntimeError, "material conversion failed"): |
||||||
|
materialtools.phong_to_pbr_metallic_roughness(material, materialtools.PhongToPbrMetallicRoughnessFlags.FAIL_ON_UNCONVERTIBLE_ATTRIBUTES) |
||||||
|
|
||||||
|
class RemoveDuplicates(unittest.TestCase): |
||||||
|
def test(self): |
||||||
|
importer = trade.ImporterManager().load_and_instantiate('GltfImporter') |
||||||
|
importer.open_file(os.path.join(os.path.dirname(__file__), "material.gltf")) |
||||||
|
a = importer.material(0) |
||||||
|
b = importer.material(1) |
||||||
|
c = importer.material(2) |
||||||
|
|
||||||
|
indices, count = materialtools.remove_duplicates([a, b, a, c, c, b]) |
||||||
|
self.assertEqual(indices, [0, 1, 0, 3, 3, 1]) |
||||||
|
self.assertEqual(count, 3) |
||||||
Loading…
Reference in new issue