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