diff --git a/src/deps/magnum-bindings b/src/deps/magnum-bindings index 7e9acb06f2..49d489382b 160000 --- a/src/deps/magnum-bindings +++ b/src/deps/magnum-bindings @@ -1 +1 @@ -Subproject commit 7e9acb06f265f73da8b5336a6f6d809541da21ce +Subproject commit 49d489382bd800e12dcec111b139fe2588c9b44d diff --git a/src/esp/bindings/AttributesBindings.cpp b/src/esp/bindings/AttributesBindings.cpp index bbb2f1f0e8..9a2792c937 100644 --- a/src/esp/bindings/AttributesBindings.cpp +++ b/src/esp/bindings/AttributesBindings.cpp @@ -10,6 +10,7 @@ #include "esp/metadata/attributes/AbstractObjectAttributes.h" #include "esp/metadata/attributes/ArticulatedObjectAttributes.h" #include "esp/metadata/attributes/AttributesBase.h" +#include "esp/metadata/attributes/AttributesEnumMaps.h" #include "esp/metadata/attributes/LightLayoutAttributes.h" #include "esp/metadata/attributes/MarkerSets.h" #include "esp/metadata/attributes/ObjectAttributes.h" @@ -17,6 +18,7 @@ #include "esp/metadata/attributes/PhysicsManagerAttributes.h" #include "esp/metadata/attributes/PrimitiveAssetAttributes.h" #include "esp/metadata/attributes/SceneInstanceAttributes.h" +#include "esp/metadata/attributes/SemanticAttributes.h" #include "esp/metadata/attributes/StageAttributes.h" namespace py = pybind11; @@ -40,6 +42,7 @@ using Attrs::MarkerSets; using Attrs::ObjectAttributes; using Attrs::PbrShaderAttributes; using Attrs::PhysicsManagerAttributes; +using Attrs::SemanticAttributes; using Attrs::StageAttributes; using Attrs::TaskSet; using Attrs::UVSpherePrimitiveAttributes; @@ -49,155 +52,57 @@ using esp::core::managedContainers::AbstractManagedObject; namespace esp { namespace metadata { -void initAttributesBindings(py::module& m) { - // ==== AbstractManagedObject ==== - // NOLINTNEXTLINE(bugprone-unused-raii) - py::class_( - m, "AbstractManagedObject"); - // ==== AbstractFileBasedManagedObject ==== - // NOLINTNEXTLINE(bugprone-unused-raii) - py::class_( - m, "AbstractFileBasedManagedObject"); +template +void declareSetterMask( + pybind11::class_< + esp::metadata::attributes::AbstractAttributes, + esp::core::managedContainers::AbstractFileBasedManagedObject, + esp::core::config::Configuration, + esp::metadata::attributes::AbstractAttributes::ptr>& pyAbsAttr, + const std::string& typeName) { + // Attributes should only use named properties or subconfigurations to set + // string values, to guarantee essential value type integrity.) + const std::string errMsg = Cr::Utility::formatString( + "Attributes should only use named properties or subconfigurations to set " + "{} values, to guarantee essential value type integrity.", + typeName); + pyAbsAttr.def( + "set", + [errMsg](CORRADE_UNUSED AbstractAttributes& self, + CORRADE_UNUSED const std::string& key, + CORRADE_UNUSED const T val) { ESP_CHECK(false, errMsg); }, + R"(This method is inherited from Configuration, but should not be used with Attributes due + to the possibility of changing the type of a required variable. Use the provided Attributes + instead to set or change values for this object.)", + "key"_a, "value"_a); +} // declareSetterMask - // ==== AbstractAttributes ==== - py::class_( - m, "AbstractAttributes") - .def(py::init( - &AbstractAttributes::create)) - .def_property("handle", &AbstractAttributes::getHandle, - &AbstractAttributes::setHandle, - R"(Name of attributes template. )") - .def_property_readonly( - "file_directory", &AbstractAttributes::getFileDirectory, - R"(Directory where file-based templates were loaded from.)") - .def_property_readonly( - "template_id", &AbstractAttributes::getID, - R"(System-generated ID for template. Will be unique among templates - of same type.)") - .def( - "get_user_config", &AbstractAttributes::editUserConfiguration, - py::return_value_policy::reference_internal, - R"(Returns a reference to the User Config object for this attributes, so that it can be - viewed or modified. Any changes to the user_config will require the owning - attributes to be re-registered.)") - .def_property_readonly( - "num_user_configs", - &AbstractAttributes::getNumUserDefinedConfigurations, - R"(The number of currently specified user-defined configuration values and - subconfigs (does not recurse subordinate subconfigs).)") - .def_property_readonly( - "total_num_user_configs", - &AbstractAttributes::getTotalNumUserDefinedConfigurations, - R"(The total number of currently specified user-defined configuration values - and subconfigs found by also recursing all subordinate subconfigs.)") - .def_property_readonly("template_class", &AbstractAttributes::getClassKey, - R"(Class name of Attributes template.)") - .def_property_readonly( - "csv_info", &AbstractAttributes::getObjectInfo, - R"(Comma-separated informational string describing this Attributes template)") +template +void declareInitializerMask( + pybind11::class_< + esp::metadata::attributes::AbstractAttributes, + esp::core::managedContainers::AbstractFileBasedManagedObject, + esp::core::config::Configuration, + esp::metadata::attributes::AbstractAttributes::ptr>& pyAbsAttr, + const std::string& typeName) { + // Attributes should only use named properties or subconfigurations to set + // string values, to guarantee essential value type integrity.) + const std::string errMsg = Cr::Utility::formatString( + "Attributes should only use named properties or subconfigurations to " + "initialize {} values, to guarantee essential value type integrity.", + typeName); + pyAbsAttr.def( + "init", + [errMsg](CORRADE_UNUSED AbstractAttributes& self, + CORRADE_UNUSED const std::string& key, + CORRADE_UNUSED const T val) { ESP_CHECK(false, errMsg); }, + R"(This method is inherited from Configuration, but should not be used with Attributes due + to the possibility of changing the expected type of a required variable. Use the provided Attributes + instead to initialize values for this object.)", + "key"_a, "value"_a); +} // declareInitializerMask - // Attributes should only use named properties or subconfigurations to set - // string values, to guarantee essential value type integrity.) - .def( - "set", - [](CORRADE_UNUSED AbstractAttributes& self, - CORRADE_UNUSED const std::string& key, - CORRADE_UNUSED const std::string& val) { - ESP_CHECK(false, - "Attributes should only use named properties or " - "subconfigurations to set string values, to " - "guarantee essential value type integrity."); - }, - R"(This method is inherited from Configuration, but should not be used with Attributes due - to the possibility of changing the type of a required variable. Use the provided Attributes - instead, to change values for this object.)", - "key"_a, "value"_a) - .def( - "set", - [](CORRADE_UNUSED AbstractAttributes& self, - CORRADE_UNUSED const std::string& key, - CORRADE_UNUSED const char* val) { - ESP_CHECK(false, - "Attributes should only use named properties or " - "subconfigurations to set string values, to " - "guarantee essential value type integrity."); - }, - R"(This method is inherited from Configuration, but should not be used with Attributes due - to the possibility of changing the type of a required variable. Use the provided Attributes - instead, to change values for this object.)", - "key"_a, "value"_a) - .def( - "set", - [](CORRADE_UNUSED AbstractAttributes& self, - CORRADE_UNUSED const std::string& key, - CORRADE_UNUSED const int val) { - ESP_CHECK(false, - "Attributes should only use named properties or " - "subconfigurations to set integer values, to " - "guarantee essential value type integrity."); - }, - R"(This method is inherited from Configuration, but should not be used with Attributes due - to the possibility of changing the type of a required variable. Use the provided Attributes - instead, to change values for this object.)", - "key"_a, "value"_a) - .def( - "set", - [](CORRADE_UNUSED AbstractAttributes& self, - CORRADE_UNUSED const std::string& key, - CORRADE_UNUSED const double val) { - ESP_CHECK(false, - "Attributes should only use named properties or " - "subconfigurations to set floating-point values, to " - "guarantee essential value type integrity."); - }, - R"(This method is inherited from Configuration, but should not be used with Attributes due - to the possibility of changing the type of a required variable. Use the provided Attributes - instead, to change values for this object.)", - "key"_a, "value"_a) - .def( - "set", - [](CORRADE_UNUSED AbstractAttributes& self, - CORRADE_UNUSED const std::string& key, - CORRADE_UNUSED const bool val) { - ESP_CHECK(false, - "Attributes should only use named properties or " - "subconfigurations to set boolean values, to " - "guarantee essential value type integrity."); - }, - R"(This method is inherited from Configuration, but should not be used with Attributes due - to the possibility of changing the type of a required variable. Use the provided Attributes - instead, to change values for this object.)", - "key"_a, "value"_a) - .def( - "set", - [](CORRADE_UNUSED AbstractAttributes& self, - CORRADE_UNUSED const std::string& key, - CORRADE_UNUSED const Magnum::Quaternion& val) { - ESP_CHECK(false, - "Attributes should only use named properties or " - "subconfigurations to set Nagnum::Quaternion values, to " - "guarantee essential value type integrity."); - }, - R"(This method is inherited from Configuration, but should not be used with Attributes due - to the possibility of changing the type of a required variable. Use the provided Attributes - instead, to change values for this object.)", - "key"_a, "value"_a) - .def( - "set", - [](CORRADE_UNUSED AbstractAttributes& self, - CORRADE_UNUSED const std::string& key, - CORRADE_UNUSED const Magnum::Vector3& val) { - ESP_CHECK(false, - "Attributes should only use named properties or " - "subconfigurations to set Magnum::Vector3 values, to " - "guarantee essential value type integrity."); - }, - R"(This method is inherited from Configuration, but should not be used with Attributes due - to the possibility of changing the type of a required variable. Use the provided Attributes - instead, to change values for this object.)", - "key"_a, "value"_a); +void initAttributesBindings(py::module& m) { // ======== Enums ================ // ==== ArticulatedObjectBaseType enum ==== @@ -206,9 +111,16 @@ void initAttributesBindings(py::module& m) { py::enum_( m, "ArticulatedObjectBaseType") .value("UNSPECIFIED", - metadata::attributes::ArticulatedObjectBaseType::Unspecified) - .value("FREE", metadata::attributes::ArticulatedObjectBaseType::Free) - .value("FIXED", metadata::attributes::ArticulatedObjectBaseType::Fixed); + metadata::attributes::ArticulatedObjectBaseType::Unspecified, + "Represents the user not specifying the type of base/root joint. " + "Resorts to any previously known/set value.") + .value("FREE", metadata::attributes::ArticulatedObjectBaseType::Free, + "The Articulated Object is joined to the world with a free joint " + "and is free to move around in the world.") + .value("FIXED", metadata::attributes::ArticulatedObjectBaseType::Fixed, + "The Articulated Object is connected to the world with a fixed " + "joint at a specific location in the world and is unable to move " + "within the world."); // ==== ArticulatedObjectInertiaSource enum ==== // Describes the source of the inertia values to use for the Articulated @@ -216,11 +128,16 @@ void initAttributesBindings(py::module& m) { py::enum_( m, "ArticulatedObjectInertiaSource") .value("UNSPECIFIED", - metadata::attributes::ArticulatedObjectInertiaSource::Unspecified) + metadata::attributes::ArticulatedObjectInertiaSource::Unspecified, + "Represents the user not specifying the source of the inertia " + "values to use. Resorts to any previously known/set value.") .value("COMPUTED", - metadata::attributes::ArticulatedObjectInertiaSource::Computed) - .value("URDF", - metadata::attributes::ArticulatedObjectInertiaSource::URDF); + metadata::attributes::ArticulatedObjectInertiaSource::Computed, + "Use inertia values computed from the collision shapes when the " + "model is loaded. This is usually more stable and is the default " + "value.") + .value("URDF", metadata::attributes::ArticulatedObjectInertiaSource::URDF, + "Use the inertia values specified in the URDF file."); // // ==== ArticulatedObjectLinkOrder enum ==== @@ -228,13 +145,69 @@ void initAttributesBindings(py::module& m) { py::enum_( m, "ArticulatedObjectLinkOrder") .value("UNSPECIFIED", - metadata::attributes::ArticulatedObjectLinkOrder::Unspecified) + metadata::attributes::ArticulatedObjectLinkOrder::Unspecified, + "Represents the user not specifying which link ordering to use. " + "Resorts to any previously known/set value") .value("URDF_ORDER", - metadata::attributes::ArticulatedObjectLinkOrder::URDFOrder) + metadata::attributes::ArticulatedObjectLinkOrder::URDFOrder, + "Use the link order derived from a tree traversal of the " + "Articulated Object.") .value("TREE_TRAVERSAL", - metadata::attributes::ArticulatedObjectLinkOrder::TreeTraversal); + metadata::attributes::ArticulatedObjectLinkOrder::TreeTraversal, + "End cap value - no Articulated Object link order enums should be " + "defined at or past this enum."); + + // + // ==== ArticulatedObjectRenderMode enum ==== + // Defines the possible options for what will be rendered for a particular + // Articulated Object. + py::enum_( + m, "ArticulatedObjectRenderMode") + .value("UNSPECIFIED", + metadata::attributes::ArticulatedObjectRenderMode::Unspecified, + "Represents the user not specifying which rendering mode to use. " + "Resorts to any previously known/set value") + .value( + "DEFAULT", metadata::attributes::ArticulatedObjectRenderMode::Default, + "Render the Articulated Object using its skin if it has one, " + "otherwise render it using the urdf-defined link meshes/primitives.") + .value("SKIN", metadata::attributes::ArticulatedObjectRenderMode::Skin, + "Render the Articulated Object using its skin.") + .value("LINK_VISUALS", + metadata::attributes::ArticulatedObjectRenderMode::LinkVisuals, + "Render the Articulated Object using urdf-defined " + "meshes/primitives to respresent each link.") + .value("NONE", metadata::attributes::ArticulatedObjectRenderMode::None, + "Do not render the Articulated Object.") + .value("BOTH", metadata::attributes::ArticulatedObjectRenderMode::Both, + "Render the Articulated Object using both the skin and the " + "urdf-defined link meshes/primitives."); // + // ==== ObjectInstanceShaderType enum ==== + // Defines the possible shader options for rendering instances of objects or + // stages in Habitat-sim. + py::enum_( + m, "ObjectInstanceShaderType") + .value( + "UNSPECIFIED", + metadata::attributes::ObjectInstanceShaderType::Unspecified, + "Represents the user not specifying which shader type choice to use. " + "Resorts to any previously known/set value") + .value("MATERIAL", + metadata::attributes::ObjectInstanceShaderType::Material, + "Override any config-specified or default shader-type values to " + "use the material-specified shader") + .value("FLAT", metadata::attributes::ObjectInstanceShaderType::Flat, + "Flat shading is pure color and no lighting. This is often used " + "for textured objects") + .value("PHONG", metadata::attributes::ObjectInstanceShaderType::Phong, + "Phong shading with diffuse, ambient and specular color " + "specifications.") + .value("PBR", metadata::attributes::ObjectInstanceShaderType::PBR, + "Physically-based rendering models the physical properties of the " + "object for rendering."); + // // ==== AssetType ==== // Describes the type of asset used for rendering, collsions, or semantics py::enum_(m, "AssetType") @@ -249,7 +222,8 @@ void initAttributesBindings(py::module& m) { // ==== Markersets and subordinate classes === py::class_( - m, "MarkerSet") + m, "MarkerSet", + R"(A hierarchical structure to manage an object's markers.)") .def(py::init(&MarkerSet::create<>)) .def_property_readonly( "num_points", &MarkerSet::getNumPoints, @@ -431,6 +405,89 @@ void initAttributesBindings(py::module& m) { keyed by LinkSet name, of dictionaries, each keyed by MarkerSet name referencing a list of that MarkerSet's marker points)"); + // ==== AbstractManagedObject ==== + // NOLINTNEXTLINE(bugprone-unused-raii) + py::class_( + m, "AbstractManagedObject"); + // ==== AbstractFileBasedManagedObject ==== + // NOLINTNEXTLINE(bugprone-unused-raii) + py::class_( + m, "AbstractFileBasedManagedObject"); + + // ==== AbstractAttributes ==== + auto pyAbsAttributes = + py::class_( + m, "AbstractAttributes"); + pyAbsAttributes + .def(py::init( + &AbstractAttributes::create)) + .def_property("handle", &AbstractAttributes::getHandle, + &AbstractAttributes::setHandle, + R"(Name of attributes template. )") + .def_property_readonly( + "file_directory", &AbstractAttributes::getFileDirectory, + R"(Directory where file-based templates were loaded from.)") + .def_property_readonly( + "template_id", &AbstractAttributes::getID, + R"(System-generated ID for template. Will be unique among templates + of same type.)") + .def( + "get_user_config", &AbstractAttributes::editUserConfiguration, + py::return_value_policy::reference_internal, + R"(Returns a reference to the User Config object for this attributes, so that it can be + viewed or modified. Any changes to the user_config will require the owning + attributes to be re-registered.)") + .def_property_readonly( + "num_user_configs", + &AbstractAttributes::getNumUserDefinedConfigurations, + R"(The number of currently specified user-defined configuration values and + subconfigs (does not recurse subordinate subconfigs).)") + .def_property_readonly( + "total_num_user_configs", + &AbstractAttributes::getTotalNumUserDefinedConfigurations, + R"(The total number of currently specified user-defined configuration values + and subconfigs found by also recursing all subordinate subconfigs.)") + .def_property_readonly("template_class", &AbstractAttributes::getClassKey, + R"(Class name of Attributes template.)") + .def_property_readonly( + "csv_info", &AbstractAttributes::getObjectInfo, + R"(Comma-separated informational string describing this Attributes template)"); + + // Attributes should only use named properties or subconfigurations to set + // specific values, to guarantee essential value type integrity. This will + // mask the underlying Configuration's raw setters + declareSetterMask(pyAbsAttributes, "string"); + declareSetterMask(pyAbsAttributes, "integer"); + declareSetterMask(pyAbsAttributes, "boolean"); + declareSetterMask(pyAbsAttributes, "floating-point"); + declareSetterMask(pyAbsAttributes, "Magnum::Vector2"); + declareSetterMask(pyAbsAttributes, "Magnum::Vector3"); + declareSetterMask(pyAbsAttributes, "Magnum::Vector4"); + declareSetterMask(pyAbsAttributes, "Magnum::Color4"); + declareSetterMask(pyAbsAttributes, "Magnum::Quaternion"); + declareSetterMask(pyAbsAttributes, "Magnum::Matrix3"); + declareSetterMask(pyAbsAttributes, "Magnum::Matrix4"); + declareSetterMask(pyAbsAttributes, "Magnum::Rad"); + + // Attributes should only use named properties or subconfigurations to + // initialize specific values, to guarantee essential value type integrity. + // This will mask the underlying Configuration's raw initializers + declareInitializerMask(pyAbsAttributes, "string"); + declareInitializerMask(pyAbsAttributes, "integer"); + declareInitializerMask(pyAbsAttributes, "boolean"); + declareInitializerMask(pyAbsAttributes, "floating-point"); + declareInitializerMask(pyAbsAttributes, "Magnum::Vector2"); + declareInitializerMask(pyAbsAttributes, "Magnum::Vector3"); + declareInitializerMask(pyAbsAttributes, "Magnum::Vector4"); + declareInitializerMask(pyAbsAttributes, "Magnum::Color4"); + declareInitializerMask(pyAbsAttributes, + "Magnum::Quaternion"); + declareInitializerMask(pyAbsAttributes, "Magnum::Matrix3"); + declareInitializerMask(pyAbsAttributes, "Magnum::Matrix4"); + declareInitializerMask(pyAbsAttributes, "Magnum::Rad"); + // ==== ArticulatedObjectAttributes ==== py::class_( @@ -683,66 +740,6 @@ void initAttributesBindings(py::module& m) { &ObjectAttributes::setSemanticId, R"(The semantic ID for objects constructed from this template.)"); - // ==== StageAttributes ==== - py::class_( - m, "StageAttributes", - R"(A metadata template for stages pre-instantiation. Defines asset paths, - collision properties, gravity direction, shader type overrides, semantic - asset information, and user defined metadata. Consumed to instantiate the - static background of a scene (e.g. the building architecture). - Is imported from .stage_config.json files.)") - .def(py::init(&StageAttributes::create<>)) - .def(py::init(&StageAttributes::create)) - .def_property( - "gravity", &StageAttributes::getGravity, &StageAttributes::setGravity, - R"(The 3-vector representation of gravity to use for physically-based - simulations on stages built from this template.)") - .def_property( - "origin", &StageAttributes::getOrigin, &StageAttributes::setOrigin, - R"(The desired location of the origin of stages built from this - template.)") - .def_property( - "semantic_orient_up", &StageAttributes::getSemanticOrientUp, - &StageAttributes::setSemanticOrientUp, - R"(Up direction for semantic stage meshes built from this template.)") - .def_property( - "semantic_orient_front", &StageAttributes::getSemanticOrientFront, - &StageAttributes::setSemanticOrientFront, - R"(Forward direction for semantic stage meshes built from this template.)") - .def_property( - "semantic_asset_handle", &StageAttributes::getSemanticAssetHandle, - &StageAttributes::setSemanticAssetHandle, - R"(Handle of the asset used for semantic segmentation of stages - built from this template.)") - .def_property_readonly( - "semantic_asset_fullpath", &StageAttributes::getSemanticAssetFullPath, - R"(Fully qualified filepath of the asset used for semantic segmentation of stages - built from this template. This filepath will only be available/accurate after - the owning attributes is registered)") - .def_property_readonly( - "semantic_asset_type", &StageAttributes::getSemanticAssetType, - R"(Type of asset used for collision calculations for constructions - built from this template.)") - .def_property( - "navmesh_asset_handle", &StageAttributes::getNavmeshAssetHandle, - &StageAttributes::setNavmeshAssetHandle, - R"(Handle of the navmesh asset used for constructions built from - this template.)") - .def_property( - "house_filename", &StageAttributes::getSemanticDescriptorFilename, - &StageAttributes::setSemanticDescriptorFilename, - R"(Handle for file containing semantic type maps and hierarchy for - constructions built from this template.)") - .def_property_readonly( - "house_fq_filename", &StageAttributes::getSemanticDescriptorFullPath, - R"(Fully qualified path of file containing semantic type maps and hierarchy for - constructions built from this template. This filepath will only be available/accurate - after the owning attributes is registered)") - .def_property( - "frustum_culling", &StageAttributes::getFrustumCulling, - &StageAttributes::setFrustumCulling, - R"(Whether frustum culling should be enabled for constructions built by this template.)"); - // ==== LightInstanceAttributes ==== py::class_( @@ -770,17 +767,50 @@ void initAttributesBindings(py::module& m) { &LightInstanceAttributes::setType, R"(The type of the light.)") .def_property( - "spot_inner_cone_angle", &LightInstanceAttributes::getInnerConeAngle, - &LightInstanceAttributes::setInnerConeAngle, + "spot_inner_cone_angle", + [](LightInstanceAttributes& self) { + return static_cast(self.getInnerConeAngle()); + }, + [](LightInstanceAttributes& self, Mn::Radd rads) { + self.setInnerConeAngle(static_cast(rads)); + }, R"(The inner cone angle to use for the dispersion of spot lights. Ignored for other types of lights.)") .def_property( - "spot_outer_cone_angle", &LightInstanceAttributes::getOuterConeAngle, - &LightInstanceAttributes::setOuterConeAngle, + "spot_outer_cone_angle", + [](LightInstanceAttributes& self) { + return static_cast(self.getOuterConeAngle()); + }, + [](LightInstanceAttributes& self, Mn::Radd rads) { + self.setOuterConeAngle(static_cast(rads)); + }, R"(The outter cone angle to use for the dispersion of spot lights. Ignored for other types of lights.)"); - // TODO : LightLayoutAttributes + // ==== LightLayoutAttributes ==== + py::class_( + m, "LightLayoutAttributes", + R"(A metadata template for a collection of light configurations, each defined by a + LightInstanceAttributes. Supports point and directional lights. Is imported from + .lighting_config.json files.)") + .def(py::init(&LightLayoutAttributes::create<>)) + .def(py::init(&LightLayoutAttributes::create)) + .def_property_readonly( + "num_lights", &LightLayoutAttributes::getNumLightInstances, + R"(The number of individual lights defined in this LightLayout)") + .def_property( + "positive_intensity_scale", + &LightLayoutAttributes::getPositiveIntensityScale, + &LightLayoutAttributes::setPositiveIntensityScale, + R"(The scale value applied to all positive intensities within this LightLayout. + This is to make simple, sweeping adjustments to scene lighting in habitat.)") + .def_property( + "negative_intensity_scale", + &LightLayoutAttributes::getNegativeIntensityScale, + &LightLayoutAttributes::setNegativeIntensityScale, + R"(The scale value applied to all negative intensities within this LightLayout. + This is to make simple, sweeping adjustments to scene lighting in habitat.)"); // ==== PbrShaderAttributes ==== py::class_( @@ -975,6 +1005,111 @@ void initAttributesBindings(py::module& m) { R"(Default restitution coefficient for contact modeling. Can be overridden by stage and object values.)"); + // ==== SemanticAttributes ==== + py::class_( + m, "SemanticAttributes", + R"(A metadata template for SemanticAttributes, which describe the various semantic assignments for a scene.)") + .def(py::init(&SemanticAttributes::create<>)) + .def(py::init(&SemanticAttributes::create)) + .def_property_readonly( + "semantic_orient_up", &SemanticAttributes::getSemanticOrientUp, + R"(Up direction for semantic meshes built from this template.)") + .def_property_readonly( + "semantic_orient_front", &SemanticAttributes::getSemanticOrientFront, + R"(Forward direction for semantic meshes built from this template.)") + .def_property_readonly( + "semantic_asset_handle", &SemanticAttributes::getSemanticAssetHandle, + R"(Handle of the asset used for semantic segmentations built from this template.)") + .def_property_readonly( + "semantic_asset_fullpath", + &SemanticAttributes::getSemanticAssetFullPath, + R"(Fully qualified filepath of the asset used for semantic segmentation + built from this template. This filepath will only be available/accurate after + the owning attributes is registered)") + .def_property_readonly( + "semantic_asset_type", &SemanticAttributes::getSemanticAssetType, + R"(Type of asset used for semantic segmentations built from this template.)") + .def_property_readonly( + "semantic_filename", + &SemanticAttributes::getSemanticDescriptorFilename, + R"(Handle for file containing semantic type maps and hierarchy for + constructions built from this template.)") + .def_property_readonly( + "semantic_fq_filename", + &SemanticAttributes::getSemanticDescriptorFullPath, + R"(Fully qualified path of file containing semantic type maps and hierarchy for + constructions built from this template. This filepath will only be available/accurate + after the owning attributes is registered)") + .def_property_readonly( + "has_textures", &SemanticAttributes::getHasSemanticTextures, + R"(Whether or not the asset described by this attributes supports texture-based semantics)") + .def_property_readonly( + "num_regions", &SemanticAttributes::getNumRegionInstances, + R"(The nmumber of semantic regions defined by this Semantic Attributes.)"); + + // ==== StageAttributes ==== + py::class_( + m, "StageAttributes", + R"(A metadata template for stages pre-instantiation. Defines asset paths, + collision properties, gravity direction, shader type overrides, semantic + asset information, and user defined metadata. Consumed to instantiate the + static background of a scene (e.g. the building architecture). + Is imported from .stage_config.json files.)") + .def(py::init(&StageAttributes::create<>)) + .def(py::init(&StageAttributes::create)) + .def_property( + "gravity", &StageAttributes::getGravity, &StageAttributes::setGravity, + R"(The 3-vector representation of gravity to use for physically-based + simulations on stages built from this template.)") + .def_property( + "origin", &StageAttributes::getOrigin, &StageAttributes::setOrigin, + R"(The desired location of the origin of stages built from this + template.)") + .def_property( + "semantic_orient_up", &StageAttributes::getSemanticOrientUp, + &StageAttributes::setSemanticOrientUp, + R"(Up direction for semantic stage meshes built from this template.)") + .def_property( + "semantic_orient_front", &StageAttributes::getSemanticOrientFront, + &StageAttributes::setSemanticOrientFront, + R"(Forward direction for semantic stage meshes built from this template.)") + .def_property( + "semantic_asset_handle", &StageAttributes::getSemanticAssetHandle, + &StageAttributes::setSemanticAssetHandle, + R"(Handle of the asset used for semantic segmentation of stages + built from this template.)") + .def_property_readonly( + "semantic_asset_fullpath", &StageAttributes::getSemanticAssetFullPath, + R"(Fully qualified filepath of the asset used for semantic segmentation of stages + built from this template. This filepath will only be available/accurate after + the owning attributes is registered)") + .def_property_readonly( + "semantic_asset_type", &StageAttributes::getSemanticAssetType, + R"(Type of asset used for semantic segmentations of stages + built from this template.)") + .def_property_readonly( + "has_textures", &StageAttributes::getHasSemanticTextures, + R"(Whether or not the asset described by this attributes supports texture-based semantics)") + .def_property( + "navmesh_asset_handle", &StageAttributes::getNavmeshAssetHandle, + &StageAttributes::setNavmeshAssetHandle, + R"(Handle of the navmesh asset used for constructions built from + this template.)") + .def_property( + "house_filename", &StageAttributes::getSemanticDescriptorFilename, + &StageAttributes::setSemanticDescriptorFilename, + R"(Handle for file containing semantic type maps and hierarchy for + constructions built from this template.)") + .def_property_readonly( + "house_fq_filename", &StageAttributes::getSemanticDescriptorFullPath, + R"(Fully qualified path of file containing semantic type maps and hierarchy for + constructions built from this template. This filepath will only be available/accurate + after the owning attributes is registered)") + .def_property( + "frustum_culling", &StageAttributes::getFrustumCulling, + &StageAttributes::setFrustumCulling, + R"(Whether frustum culling should be enabled for constructions built by this template.)"); + // ==== AbstractPrimitiveAttributes ==== py::class_(m, "AbstractPrimitiveAttributes") diff --git a/src/esp/bindings/AttributesManagersBindings.cpp b/src/esp/bindings/AttributesManagersBindings.cpp index 0572e8761d..cb35b064e2 100644 --- a/src/esp/bindings/AttributesManagersBindings.cpp +++ b/src/esp/bindings/AttributesManagersBindings.cpp @@ -11,6 +11,7 @@ #include "esp/metadata/attributes/AbstractObjectAttributes.h" #include "esp/metadata/attributes/LightLayoutAttributes.h" #include "esp/metadata/attributes/ObjectAttributes.h" +#include "esp/metadata/attributes/SemanticAttributes.h" #include "esp/metadata/attributes/StageAttributes.h" #include "esp/metadata/managers/AOAttributesManager.h" @@ -416,8 +417,8 @@ void initAttributesManagersBindings(py::module& m) { // ==== Light Layout Attributes Template manager ==== declareBaseAttributesManager(m, "LightLayout", - "BaseLightLayout"); + ManagedObjectAccess::Copy>( + m, "LightLayoutAttributes", "BaseLightLayout"); // NOLINTNEXTLINE(bugprone-unused-raii) py::class_< LightLayoutAttributesManager, @@ -497,17 +498,6 @@ void initAttributesManagersBindings(py::module& m) { R"(Returns the handle for a random synthesized(primitive asset)-based template chosen from the existing ObjectAttributes templates being managed.)"); - // ==== Stage Attributes Template manager ==== - declareBaseAttributesManager( - m, "StageAttributes", "BaseStage"); - // NOLINTNEXTLINE(bugprone-unused-raii) - py::class_, - StageAttributesManager::ptr>( - m, "StageAttributesManager", - R"(Manages StageAttributes which define metadata for stages (i.e. static background mesh such - as architectural elements) pre-instantiation. Can import .stage_config.json files.)"); - // ==== Physics World/Manager Template manager ==== declareBaseAttributesManager( + m, "StageAttributes", "BaseStage"); + // NOLINTNEXTLINE(bugprone-unused-raii) + py::class_, + StageAttributesManager::ptr>( + m, "StageAttributesManager", + R"(Manages StageAttributes which define metadata for stages (i.e. static background mesh such + as architectural elements) pre-instantiation. Can import .stage_config.json files.)"); + } // initAttributesManagersBindings } // namespace managers } // namespace metadata diff --git a/src/esp/bindings/Bindings.cpp b/src/esp/bindings/Bindings.cpp index 5edc10aef2..18a9482ef6 100644 --- a/src/esp/bindings/Bindings.cpp +++ b/src/esp/bindings/Bindings.cpp @@ -9,6 +9,7 @@ #include "esp/core/Configuration.h" #include "esp/core/Esp.h" #include "esp/core/RigidState.h" +#include "esp/gfx/Renderer.h" namespace py = pybind11; using py::literals::operator""_a; @@ -58,24 +59,36 @@ PYBIND11_MODULE(habitat_sim_bindings, m) { py::bind_map>(m, "MapStringString"); - // NOTE(msb) These need to be run in dependency order. - // TODO(msb) gfx, scene, and sensor should not cross-depend - // TODO(msb) sim and sensor should not cross-depend esp::initEspBindings(m); esp::core::config::initConfigBindings(m); esp::core::initCoreBindings(m); esp::geo::initGeoBindings(m); - esp::scene::initSceneBindings(m); - esp::gfx::initGfxBindings(m); - esp::gfx::replay::initGfxReplayBindings(m); + + // To address circular references, we build certain class binding before + // other bindings that reference it, and then complete its definition that + // includes references to those classes. + auto pySceneNode = esp::scene::createSceneNodeBind(m); + auto pyRenderCamera = esp::gfx::createRenderCameraBind(m); + auto pyRenderer = esp::gfx::createRendererBind(m); + esp::gfx::initRenderTargetBind(m); + // Sensor depends on SceneNode, RenderCamera and RenderTarget bindings esp::sensor::initSensorBindings(m); + esp::gfx::initGfxBindings(m, pyRenderCamera); + + esp::gfx::replay::initGfxReplayBindings(m); + // We pass the created scene node class binding to the initialization function + // to complete its definition + esp::scene::initSceneBindings(m, pySceneNode); esp::nav::initShortestPathBindings(m); + esp::sim::initSimConfigBindings(m); esp::metadata::initAttributesBindings(m); - esp::metadata::initMetadataMediatorBindings(m); esp::metadata::managers::initAttributesManagersBindings(m); + esp::metadata::initMetadataMediatorBindings(m); // These depend on SceneNode bindings esp::physics::initPhysicsBindings(m); esp::physics::initPhysicsObjectBindings(m); esp::physics::initPhysicsWrapperManagerBindings(m); esp::sim::initSimBindings(m); + // Renderer relies on simulator class bindings + esp::gfx::finalInitRenderer(pyRenderer); } diff --git a/src/esp/bindings/Bindings.h b/src/esp/bindings/Bindings.h index 536163d330..3473d57cb2 100644 --- a/src/esp/bindings/Bindings.h +++ b/src/esp/bindings/Bindings.h @@ -5,6 +5,7 @@ #ifndef ESP_BINDINGS_BINDINGS_H_ #define ESP_BINDINGS_BINDINGS_H_ +#include #include #include "esp/bindings/OpaqueTypes.h" @@ -33,10 +34,51 @@ void initGeoBindings(pybind11::module& m); namespace gfx { +/** + * @brief Create pybind class for RenderCamera, and partially define bindings. + * Bindings should be completed in @ref initGfxBindings, once dependent + * bindings have been created (i.e. SceneNode) + */ +pybind11::class_, + Magnum::SceneGraph::Camera3D, + Magnum::SceneGraph::PyFeatureHolder> +createRenderCameraBind(pybind11::module& m); + +class Renderer; + +/** + * @brief Create pybind class for Renderer, and partially define bindings. + * Bindings should be completed in @ref initGfxBindings, once dependent + * bindings have been created (i.e. SceneNode) + */ +pybind11::class_> +createRendererBind(pybind11::module& m); + +/** + * @brief Finalize Renderer bindings definitions after sim bindings class + * defined. + */ +void finalInitRenderer( + pybind11::class_>& renderer); + +/** + * @brief Specify bindings for RenderTarget. Done separately so that it can be + * performed before Sensor bindings are defined, which depend on it. + */ +void initRenderTargetBind(pybind11::module& m); + /** * @brief Specify bindings for constructs in esp::gfx namespace */ -void initGfxBindings(pybind11::module& m); +void initGfxBindings( + pybind11::module& m, + pybind11::class_, + Magnum::SceneGraph::Camera3D, + Magnum::SceneGraph::PyFeatureHolder>& + renderCamera); + namespace replay { /** * @brief Specify bindings for constructs in esp::gfx::replay namespace @@ -96,6 +138,16 @@ void initPhysicsWrapperManagerBindings(pybind11::module& m); } // namespace physics namespace scene { + +pybind11::class_< + esp::scene::SceneNode, + Magnum::SceneGraph::PyObject, + Magnum::SceneGraph::Object< + Magnum::SceneGraph::BasicTranslationRotationScalingTransformation3D< + float>>, + Magnum::SceneGraph::PyObjectHolder> +createSceneNodeBind(pybind11::module& m); + /** * @brief Specify bindings for @ref esp::scene::SceneNode , @ref esp::scene::SceneGraph , * @ref esp::scene::SceneManager , @ref esp::scene::SemanticCategory , @@ -104,7 +156,16 @@ namespace scene { * @ref esp::scene::SemanticLevel , @ref esp::scene::SemanticScene , and * @ref esp::scene::ObjectControls */ -void initSceneBindings(pybind11::module& m); +void initSceneBindings( + pybind11::module& m, + pybind11::class_< + esp::scene::SceneNode, + Magnum::SceneGraph::PyObject, + Magnum::SceneGraph::Object< + Magnum::SceneGraph::BasicTranslationRotationScalingTransformation3D< + float>>, + Magnum::SceneGraph::PyObjectHolder>& + pySceneNode); } // namespace scene namespace sensor { @@ -116,10 +177,14 @@ void initSensorBindings(pybind11::module& m); namespace sim { /** - * @brief Specify bindings for @ref esp::sim::SimulatorConfiguration , @ref esp::sim::Simulator , - * @ref esp::sim::ReplayRendererConfiguration , @ref esp::sim::AbstractReplayRenderer , + * @brief Specify bindings for @ref esp::sim::Simulator and @ref esp::sim::AbstractReplayRenderer , */ void initSimBindings(pybind11::module& m); +/** + * @brief Specify bindings for @ref esp::sim::SimulatorConfiguration and + * @ref esp::sim::ReplayRendererConfiguration + */ +void initSimConfigBindings(pybind11::module& m); } // namespace sim } // namespace esp diff --git a/src/esp/bindings/ConfigBindings.cpp b/src/esp/bindings/ConfigBindings.cpp index 4f72c84c5d..cbfdb65f07 100644 --- a/src/esp/bindings/ConfigBindings.cpp +++ b/src/esp/bindings/ConfigBindings.cpp @@ -37,11 +37,43 @@ py::object getObjectForConfigValue(const ConfigValue& value) { case ConfigValType::MagnumQuat: return py::cast(value.get()); case ConfigValType::MagnumRad: - return py::cast(value.get()); + return py::cast(static_cast(value.get())); } return py::cast(nullptr); } +template +void declareSetter( + pybind11::class_& pyAbsAttr, + const std::string& typeName) { + pyAbsAttr.def( + "set", + [](Configuration& self, const std::string& key, const T val) { + self.set(key, val); + }, + ("Set the value specified by given string key to be specified " + + typeName + " value.") + .c_str(), + "key"_a, "value"_a); +} // declareSetter + +template +void declareInitializer( + pybind11::class_& pyAbsAttr, + const std::string& typeName) { + pyAbsAttr.def( + "init", + [](Configuration& self, const std::string& key, const T val) { + self.init(key, val); + }, + ("Initialize the value specified by given string key to be specified " + + typeName + " value.") + .c_str(), + "key"_a, "value"_a); +} // declareInitializer + void initConfigBindings(py::module& m) { py::enum_(m, "ConfigValType") .value("Unknown", ConfigValType::Unknown) @@ -52,13 +84,15 @@ void initConfigBindings(py::module& m) { .value("MagnumVec2", ConfigValType::MagnumVec2) .value("MagnumVec3", ConfigValType::MagnumVec3) .value("MagnumVec4", ConfigValType::MagnumVec4) + .value("MagnumQuat", ConfigValType::MagnumQuat) .value("MagnumMat3", ConfigValType::MagnumMat3) .value("MagnumMat4", ConfigValType::MagnumMat4) - .value("MagnumQuat", ConfigValType::MagnumQuat) .value("MagnumRad", ConfigValType::MagnumRad); - py::class_(m, "Configuration") - .def(py::init(&Configuration::create<>)) + auto pyConfiguration = + py::class_(m, "Configuration"); + pyConfiguration.def(py::init(&Configuration::create<>)) + .def("__repr__", &Configuration::getAllValsAsString, "new_line"_a = "\n") .def_property_readonly( "top_level_num_entries", &Configuration::getNumEntries, R"(Holds the total number of values and subconfigs this Configuration holds @@ -89,77 +123,6 @@ void initConfigBindings(py::module& m) { return getObjectForConfigValue(self.get(key)); }, R"(Retrieve the requested value referenced by key argument, if it exists)") - - .def( - "set", - [](Configuration& self, const std::string& key, - const std::string& val) { self.set(key, val); }, - R"(Set the value specified by given string key to be specified string value)", - "key"_a, "value"_a) - .def( - "set", - [](Configuration& self, const std::string& key, const char* val) { - self.set(key, val); - }, - R"(Set the value specified by given string key to be specified string value)", - "key"_a, "value"_a) - .def( - "set", - [](Configuration& self, const std::string& key, const bool val) { - self.set(key, val); - }, - R"(Set the value specified by given string key to be specified boolean value)", - "key"_a, "value"_a) - .def( - "set", - [](Configuration& self, const std::string& key, const int val) { - self.set(key, val); - }, - R"(Set the value specified by given string key to be specified integer value)", - "key"_a, "value"_a) - .def( - "set", - [](Configuration& self, const std::string& key, const double val) { - self.set(key, val); - }, - R"(Set the value specified by given string key to be specified double value)", - "key"_a, "value"_a) - .def( - "set", - [](Configuration& self, const std::string& key, - const Magnum::Quaternion& val) { self.set(key, val); }, - R"(Set the value specified by given string key to be specified Magnum::Quaternion value)", - "key"_a, "value"_a) - .def( - "set", - [](Configuration& self, const std::string& key, - const Magnum::Vector2& val) { self.set(key, val); }, - R"(Set the value specified by given string key to be specified Magnum::Vector2 value)", - "key"_a, "value"_a) - .def( - "set", - [](Configuration& self, const std::string& key, - const Magnum::Vector3& val) { self.set(key, val); }, - R"(Set the value specified by given string key to be specified Magnum::Vector3 value)", - "key"_a, "value"_a) - .def( - "set", - [](Configuration& self, const std::string& key, - const Magnum::Vector4& val) { self.set(key, val); }, - R"(Set the value specified by given string key to be specified Magnum::Vector4 value)", - "key"_a, "value"_a) - .def( - "set", - [](Configuration& self, const std::string& key, - const Magnum::Matrix3& val) { self.set(key, val); }, - R"(Set the value specified by given string key to be specified Magnum::Matrix3 value)", - "key"_a, "value"_a) - .def( - "set", - [](Configuration& self, const std::string& key, - const Magnum::Matrix4& val) { self.set(key, val); }, - R"(Set the value specified by given string key to be specified Magnum::Matrix4 value)", - "key"_a, "value"_a) .def( "get_type", &Configuration::getType, R"(Retrieves the ConfigValType of the value referred to by the passed key.)") @@ -222,8 +185,53 @@ void initConfigBindings(py::module& m) { R"(Returns true if specified key references an existing subconfiguration within this configuration.)") .def( "remove_subconfig", &Configuration::removeSubconfig, - R"(Removes and returns subconfiguration corresponding to passed key, if found. Gives warning otherwise.)") - .def("__repr__", &Configuration::getAllValsAsString, "new_line"_a = "\n"); + R"(Removes and returns subconfiguration corresponding to passed key, if found. Gives warning otherwise.)"); + // Setter bindings + declareSetter(pyConfiguration, "string"); + declareSetter(pyConfiguration, "boolean"); + declareSetter(pyConfiguration, "integer"); + declareSetter(pyConfiguration, "floating-point"); + declareSetter(pyConfiguration, "Magnum::Vector2"); + declareSetter(pyConfiguration, "Magnum::Vector3"); + declareSetter(pyConfiguration, "Magnum::Vector4"); + declareSetter(pyConfiguration, "Magnum::Color4"); + declareSetter(pyConfiguration, "Magnum::Quaternion"); + declareSetter(pyConfiguration, "Magnum::Matrix3"); + declareSetter(pyConfiguration, "Magnum::Matrix4"); + // Use Radd version for bindings + pyConfiguration.def( + "set", + [](Configuration& self, const std::string& key, const Mn::Radd val) { + self.set(key, static_cast(val)); + }, + "Set the value specified by given string key to be specified Magnum::Rad " + "value.", + "key"_a, "value"_a); + + // Initializer bindings + // Initializers are like setters but the value specified will not be + // automatically saved to file unless it is changed. + declareInitializer(pyConfiguration, "string"); + declareInitializer(pyConfiguration, "boolean"); + declareInitializer(pyConfiguration, "integer"); + declareInitializer(pyConfiguration, "floating-point"); + declareInitializer(pyConfiguration, "Magnum::Vector2"); + declareInitializer(pyConfiguration, "Magnum::Vector3"); + declareInitializer(pyConfiguration, "Magnum::Vector4"); + declareInitializer(pyConfiguration, "Magnum::Color4"); + declareInitializer(pyConfiguration, + "Magnum::Quaternion"); + declareInitializer(pyConfiguration, "Magnum::Matrix3"); + declareInitializer(pyConfiguration, "Magnum::Matrix4"); + // Use Radd version for bindings + pyConfiguration.def( + "init", + [](Configuration& self, const std::string& key, const Mn::Radd val) { + self.init(key, static_cast(val)); + }, + "Initialize the value specified by given string key to be specified " + "Magnum::Rad value.", + "key"_a, "value"_a); } // initConfigBindings diff --git a/src/esp/bindings/GfxBindings.cpp b/src/esp/bindings/GfxBindings.cpp index 80d733eced..220089657c 100644 --- a/src/esp/bindings/GfxBindings.cpp +++ b/src/esp/bindings/GfxBindings.cpp @@ -38,25 +38,27 @@ esp::scene::SceneNode* nodeGetter(T& self) { namespace esp { namespace gfx { - -void initGfxBindings(py::module& m) { - // ==== RenderCamera ==== +py::class_, + Magnum::SceneGraph::Camera3D, + Magnum::SceneGraph::PyFeatureHolder> +createRenderCameraBind(py::module& m) { py::class_, Magnum::SceneGraph::Camera3D, Magnum::SceneGraph::PyFeatureHolder> - render_camera( + renderCamera( m, "Camera", R"(RenderCamera: The object of this class is a camera attached to the scene node for rendering.)"); - py::enum_ flags{render_camera, "Flags", "Flags"}; + py::enum_ flags{renderCamera, "Flags", "Flags"}; flags.value("FRUSTUM_CULLING", RenderCamera::Flag::FrustumCulling) .value("OBJECTS_ONLY", RenderCamera::Flag::ObjectsOnly) .value("NONE", RenderCamera::Flag{}); pybindEnumOperators(flags); - render_camera + renderCamera .def( "set_projection_matrix", [](RenderCamera& self, int w, int h, float n, float f, Mn::Degd fov) { @@ -70,14 +72,15 @@ void initGfxBindings(py::module& m) { "height"_a, "znear"_a, "zfar"_a, "scale"_a) .def( "unproject", &RenderCamera::unproject, - R"(Unproject a 2D viewport point to a 3D ray with its origin at the camera position. Ray direction is optionally normalized. Non-normalized rays originate at the camera location and terminate at a view plane one unit down the Z axis.)", - "viewport_point"_a, "normalized"_a = true) - .def_property_readonly("node", nodeGetter, - "Node this object is attached to") - .def_property_readonly("object", nodeGetter, - "Alias to node"); + R"(Unproject a 2D viewport point to a 3D ray with its origin at the camera + position. Ray direction is optionally normalized. Non-normalized rays + originate at the camera location and terminate at a view plane one unit down the Z axis.)", + "viewport_point"_a, "normalized"_a = true); - // ==== Renderer ==== + return renderCamera; +} // createRenderCameraBind + +py::class_ createRendererBind(py::module& m) { py::class_ renderer(m, "Renderer"); py::enum_ rendererFlags{renderer, "Flags", "Flags"}; @@ -86,6 +89,12 @@ void initGfxBindings(py::module& m) { .value("NONE", Renderer::Flag{}); pybindEnumOperators(rendererFlags); + return renderer; +} // createRendererBind + +void finalInitRenderer(py::class_& renderer) { + // ==== Renderer ==== + // Add additional bindings for simulator and sensor bindings access renderer.def(py::init(&Renderer::create<>)) .def( "draw", @@ -130,7 +139,11 @@ void initGfxBindings(py::module& m) { R"(Binds a RenderTarget to the sensor)", "visualSensor"_a, "flags"_a = Renderer::Flag{}); - py::class_(m, "RenderTarget") +} // finalInitRenderer + +void initRenderTargetBind(py::module& m) { + auto pyRenderTarget = py::class_(m, "RenderTarget"); + pyRenderTarget .def("__enter__", [](RenderTarget& self) { self.renderEnter(); @@ -176,6 +189,24 @@ void initGfxBindings(py::module& m) { .def("render_enter", &RenderTarget::renderEnter) .def("render_exit", &RenderTarget::renderExit); +} // initRenderTargetBind + +void initGfxBindings( + py::module& m, + py::class_, + Magnum::SceneGraph::Camera3D, + Magnum::SceneGraph::PyFeatureHolder>& + renderCamera) { + // ==== RenderCamera ==== + // Add additional bindings for SceneNode access after SceneNode bindings are + // defined + renderCamera + .def_property_readonly("node", nodeGetter, + "Node this object is attached to") + .def_property_readonly("object", nodeGetter, + "Alias to node"); + py::enum_( m, "LightPositionModel", R"(Defines the coordinate frame of a light source.)") @@ -188,6 +219,7 @@ void initGfxBindings(py::module& m) { .value("Point", LightType::Point) .value("Directional", LightType::Directional); + // ========== LightInfo ============= py::class_( m, "LightInfo", R"(Defines the vector, color and LightPositionModel of a single light source. @@ -196,13 +228,14 @@ void initGfxBindings(py::module& m) { .def(py::init()) .def(py::init(), "vector"_a, "color"_a = Magnum::Color3{1}, - "model"_a = LightPositionModel::Global) + py::arg_v("model", LightPositionModel::Global, "LightPositionModel")) .def_readwrite("vector", &LightInfo::vector) .def_readwrite("color", &LightInfo::color) .def_readwrite("model", &LightInfo::model) .def(py::self == py::self) .def(py::self != py::self); + // ========== DebugLineRender ============ py::class_>( m, "DebugLineRender") .def( diff --git a/src/esp/bindings/PhysicsObjectBindings.cpp b/src/esp/bindings/PhysicsObjectBindings.cpp index 7263684817..eb046db50b 100644 --- a/src/esp/bindings/PhysicsObjectBindings.cpp +++ b/src/esp/bindings/PhysicsObjectBindings.cpp @@ -457,12 +457,13 @@ void declareArticulatedObjectWrapper(py::module& m, "link_id. Use link_id==-1 to get the base link.") .c_str(), "link_id"_a) - .def("get_link", &ManagedArticulatedObject::getLink, - ("Get this " + objType + - "'s articulated link specified by the passed " - "link_id. Use link_id==-1 to get the base link.") - .c_str(), - "link_id"_a) + // No binding class currently exists for the Articulated Link + // .def("get_link", &ManagedArticulatedObject::getLink, + // ("Get this " + objType + + // "'s articulated link specified by the passed " + // "link_id. Use link_id==-1 to get the base link.") + // .c_str(), + // "link_id"_a) .def( "get_link_ids", &ManagedArticulatedObject::getLinkIds, ("Get a list of this " + objType + "'s individual link ids.").c_str()) diff --git a/src/esp/bindings/SceneBindings.cpp b/src/esp/bindings/SceneBindings.cpp index 6b0db7bb53..76be14f7e6 100644 --- a/src/esp/bindings/SceneBindings.cpp +++ b/src/esp/bindings/SceneBindings.cpp @@ -9,7 +9,6 @@ #include #include - #include "esp/scene/Mp3dSemanticScene.h" #include "esp/scene/ObjectControls.h" #include "esp/scene/SceneGraph.h" @@ -19,10 +18,61 @@ namespace py = pybind11; using py::literals::operator""_a; +namespace Mn = Magnum; namespace esp { namespace scene { -void initSceneBindings(py::module& m) { +py::class_, + Magnum::SceneGraph::Object< + Magnum::SceneGraph:: + BasicTranslationRotationScalingTransformation3D>, + Magnum::SceneGraph::PyObjectHolder> +createSceneNodeBind(py::module& m) { + // ==== SceneNode ==== + py::class_, + Magnum::SceneGraph::Object< + Magnum::SceneGraph:: + BasicTranslationRotationScalingTransformation3D>, + Magnum::SceneGraph::PyObjectHolder> + pySceneNode(m, "SceneNode", R"( + SceneNode: a node in the scene graph. + + Cannot apply a smart pointer to a SceneNode object. + You can "create it and forget it". + Simulator backend will handle the memory.)"); + + py::class_(m, "SceneGraph") + .def(py::init()) + .def("get_root_node", + py::overload_cast<>(&SceneGraph::getRootNode, py::const_), + R"( + Get the root node of the scene graph. + + User can specify transformation of the root node w.r.t. the world + frame. (const function) PYTHON DOES NOT GET OWNERSHIP)", + py::return_value_policy::reference) + .def("get_root_node", py::overload_cast<>(&SceneGraph::getRootNode), + R"( + Get the root node of the scene graph. + + User can specify transformation of the root node w.r.t. the world + frame. PYTHON DOES NOT GET OWNERSHIP)", + py::return_value_policy::reference); + + return pySceneNode; +} // createSceneNodeBind + +void initSceneBindings( + py::module& m, + py::class_, + Magnum::SceneGraph::Object< + Magnum::SceneGraph:: + BasicTranslationRotationScalingTransformation3D>, + Magnum::SceneGraph::PyObjectHolder>& + pySceneNode) { // ==== SceneGraph ==== // !!Warning!! @@ -41,14 +91,7 @@ void initSceneBindings(py::module& m) { .value("CAMERA", SceneNodeType::CAMERA) .value("OBJECT", SceneNodeType::OBJECT); - // ==== SceneNode ==== - py::class_, MagnumObject, - Magnum::SceneGraph::PyObjectHolder>(m, "SceneNode", R"( - SceneNode: a node in the scene graph. - - Cannot apply a smart pointer to a SceneNode object. - You can "create it and forget it". - Simulator backend will handle the memory.)") + pySceneNode .def(py::init_alias>(), R"(Constructor: creates a scene node, and sets its parent.)") .def_property("type", &SceneNode::getType, &SceneNode::setType) @@ -93,24 +136,6 @@ void initSceneBindings(py::module& m) { .def_property_readonly("subtree_sensors", &SceneNode::getSubtreeSensors, R"(Get subtree sensors of this SceneNode)"); - py::class_(m, "SceneGraph") - .def(py::init()) - .def("get_root_node", - py::overload_cast<>(&SceneGraph::getRootNode, py::const_), - R"( - Get the root node of the scene graph. - - User can specify transformation of the root node w.r.t. the world - frame. (const function) PYTHON DOES NOT GET OWNERSHIP)", - pybind11::return_value_policy::reference) - .def("get_root_node", py::overload_cast<>(&SceneGraph::getRootNode), - R"( - Get the root node of the scene graph. - - User can specify transformation of the root node w.r.t. the world - frame. PYTHON DOES NOT GET OWNERSHIP)", - pybind11::return_value_policy::reference); - // ==== SceneManager ==== py::class_(m, "SceneManager") .def("init_scene_graph", &SceneManager::initSceneGraph, @@ -122,14 +147,14 @@ void initSceneBindings(py::module& m) { Get the scene graph by scene graph ID. PYTHON DOES NOT GET OWNERSHIP)", - "sceneGraphID"_a, pybind11::return_value_policy::reference) + "sceneGraphID"_a, py::return_value_policy::reference) .def("get_scene_graph", py::overload_cast(&SceneManager::getSceneGraph, py::const_), R"( Get the scene graph by scene graph ID. PYTHON DOES NOT GET OWNERSHIP)", - "sceneGraphID"_a, pybind11::return_value_policy::reference); + "sceneGraphID"_a, py::return_value_policy::reference); // ==== SemanticCategory ==== py::class_(m, "SemanticCategory") diff --git a/src/esp/bindings/SensorBindings.cpp b/src/esp/bindings/SensorBindings.cpp index daa5059aa4..4c6e4f85ab 100644 --- a/src/esp/bindings/SensorBindings.cpp +++ b/src/esp/bindings/SensorBindings.cpp @@ -163,11 +163,17 @@ void initSensorBindings(py::module& m) { .def_readwrite("alpha", &FisheyeSensorDoubleSphereSpec::alpha) .def_readwrite("xi", &FisheyeSensorDoubleSphereSpec::xi); - // ==== SensorFactory ==== - py::class_(m, "SensorFactory") - .def("create_sensors", &SensorFactory::createSensors) - .def("delete_sensor", &SensorFactory::deleteSensor) - .def("delete_subtree_sensor", &SensorFactory::deleteSubtreeSensor); + // ==== Sensor ==== + py::class_, + Magnum::SceneGraph::AbstractFeature3D, + Magnum::SceneGraph::PyFeatureHolder>(m, "Sensor") + .def("specification", &Sensor::specification) + .def("set_transformation_from_spec", &Sensor::setTransformationFromSpec) + .def("is_visual_sensor", &Sensor::isVisualSensor) + .def("get_observation", &Sensor::getObservation) + .def_property_readonly("node", nodeGetter, + "Node this object is attached to") + .def_property_readonly("object", nodeGetter, "Alias to node"); // ==== SensorSuite ==== py::class_, @@ -185,17 +191,11 @@ void initSensorBindings(py::module& m) { "Node this object is attached to") .def_property_readonly("object", nodeGetter, "Alias to node"); - // ==== Sensor ==== - py::class_, - Magnum::SceneGraph::AbstractFeature3D, - Magnum::SceneGraph::PyFeatureHolder>(m, "Sensor") - .def("specification", &Sensor::specification) - .def("set_transformation_from_spec", &Sensor::setTransformationFromSpec) - .def("is_visual_sensor", &Sensor::isVisualSensor) - .def("get_observation", &Sensor::getObservation) - .def_property_readonly("node", nodeGetter, - "Node this object is attached to") - .def_property_readonly("object", nodeGetter, "Alias to node"); + // ==== SensorFactory ==== + py::class_(m, "SensorFactory") + .def("create_sensors", &SensorFactory::createSensors) + .def("delete_sensor", &SensorFactory::deleteSensor) + .def("delete_subtree_sensor", &SensorFactory::deleteSubtreeSensor); // ==== VisualSensor ==== py::class_, Sensor, diff --git a/src/esp/bindings/SimBindings.cpp b/src/esp/bindings/SimBindings.cpp index f50938d9b8..4e8b7c01ba 100644 --- a/src/esp/bindings/SimBindings.cpp +++ b/src/esp/bindings/SimBindings.cpp @@ -29,7 +29,7 @@ using py::literals::operator""_a; namespace esp { namespace sim { -void initSimBindings(py::module& m) { +void initSimConfigBindings(py::module& m) { // ==== SimulatorConfiguration ==== py::class_( m, "SimulatorConfiguration") @@ -105,6 +105,40 @@ void initSimBindings(py::module& m) { .def(py::self == py::self) .def(py::self != py::self); + // ==== ReplayRendererConfiguration ==== + py::class_( + m, "ReplayRendererConfiguration") + .def(py::init(&ReplayRendererConfiguration::create<>)) + .def_readwrite("num_environments", + &ReplayRendererConfiguration::numEnvironments, + R"(Number of concurrent environments to render.)") + .def_readwrite( + "standalone", &ReplayRendererConfiguration::standalone, + R"(Determines if the renderer is standalone (windowless) or not (embedded in another window).)") + .def_readwrite( + "sensor_specifications", + &ReplayRendererConfiguration::sensorSpecifications, + R"(List of sensor specifications for one simulator. For batch rendering, all simulators must have the same specification.)") + .def_readwrite("gpu_device_id", &ReplayRendererConfiguration::gpuDeviceId, + R"(The system GPU device to use for rendering)") + .def_readwrite("enable_frustum_culling", + &ReplayRendererConfiguration::enableFrustumCulling, + R"(Controls whether frustum culling is enabled.)") + .def_readwrite( + "enable_hbao", &ReplayRendererConfiguration::enableHBAO, + R"(Controls whether horizon-based ambient occlusion is enabled.)") + .def_readwrite( + "force_separate_semantic_scene_graph", + &ReplayRendererConfiguration::forceSeparateSemanticSceneGraph, + R"(Required to support playback of any gfx replay that includes a + stage with a semantic mesh. Set to false otherwise.)") + .def_readwrite( + "leave_context_with_background_renderer", + &ReplayRendererConfiguration::leaveContextWithBackgroundRenderer, + R"(See See tutorials/async_rendering.py.)"); +} + +void initSimBindings(py::module& m) { // ==== Simulator ==== py::class_(m, "Simulator") // modify constructor to pass MetadataMediator @@ -361,38 +395,6 @@ void initSimBindings(py::module& m) { pybind11::return_value_policy::reference, R"(Get visualization helper for rendering lines.)"); - // ==== ReplayRendererConfiguration ==== - py::class_( - m, "ReplayRendererConfiguration") - .def(py::init(&ReplayRendererConfiguration::create<>)) - .def_readwrite("num_environments", - &ReplayRendererConfiguration::numEnvironments, - R"(Number of concurrent environments to render.)") - .def_readwrite( - "standalone", &ReplayRendererConfiguration::standalone, - R"(Determines if the renderer is standalone (windowless) or not (embedded in another window).)") - .def_readwrite( - "sensor_specifications", - &ReplayRendererConfiguration::sensorSpecifications, - R"(List of sensor specifications for one simulator. For batch rendering, all simulators must have the same specification.)") - .def_readwrite("gpu_device_id", &ReplayRendererConfiguration::gpuDeviceId, - R"(The system GPU device to use for rendering)") - .def_readwrite("enable_frustum_culling", - &ReplayRendererConfiguration::enableFrustumCulling, - R"(Controls whether frustum culling is enabled.)") - .def_readwrite( - "enable_hbao", &ReplayRendererConfiguration::enableHBAO, - R"(Controls whether horizon-based ambient occlusion is enabled.)") - .def_readwrite( - "force_separate_semantic_scene_graph", - &ReplayRendererConfiguration::forceSeparateSemanticSceneGraph, - R"(Required to support playback of any gfx replay that includes a - stage with a semantic mesh. Set to false otherwise.)") - .def_readwrite( - "leave_context_with_background_renderer", - &ReplayRendererConfiguration::leaveContextWithBackgroundRenderer, - R"(See See tutorials/async_rendering.py.)"); - // ==== ReplayRenderer ==== py::class_( m, "ReplayRenderer") diff --git a/src/esp/metadata/attributes/LightLayoutAttributes.h b/src/esp/metadata/attributes/LightLayoutAttributes.h index 93a1c0931b..fa3394a150 100644 --- a/src/esp/metadata/attributes/LightLayoutAttributes.h +++ b/src/esp/metadata/attributes/LightLayoutAttributes.h @@ -116,7 +116,7 @@ class LightInstanceAttributes : public AbstractAttributes { /** * @brief Gets a smart pointer reference to a copy of the spotlight - * configuration data for this light instance. + * configuration data for this @ref LightInstanceAttributes. */ std::shared_ptr getSpotlightConfiguration() const { return getSubconfigCopy("spot"); @@ -124,7 +124,7 @@ class LightInstanceAttributes : public AbstractAttributes { /** * @brief Gets a smart pointer reference to the actual spotlight - * configuration data for this light instance. + * configuration data for this @ref LightInstanceAttributes. */ std::shared_ptr editSpotlightConfiguration() { return editSubconfig("spot"); @@ -248,7 +248,7 @@ class LightLayoutAttributes : public AbstractAttributes { } /** - * @brief Add a light instance to this lighting layout + * @brief Add a @ref LightInstanceAttributes to this lighting layout */ void addLightInstance(LightInstanceAttributes::ptr _lightInstance) { this->setSubAttributesInternal( @@ -256,20 +256,23 @@ class LightLayoutAttributes : public AbstractAttributes { } /** - * @brief Remove a light from this lighting layout + * @brief Remove a named @ref LightInstanceAttributes from this lighting layout */ LightInstanceAttributes::ptr removeLightInstance(const std::string& handle) { return this->removeNamedSubAttributesInternal( handle, availableLightIDs_, lightInstConfig_); } + /** + * @brief Retrieve a reference to the named @ref LightInstanceAttributes + */ LightInstanceAttributes::cptr getLightInstance(const std::string& handle) { return getNamedSubAttributesInternal( handle, lightInstConfig_); } /** - * @brief Get the lighting instances for this layout + * @brief Get all the @ref LightInstanceAttributes for this layout */ std::vector getLightInstances() const { return this->getSubAttributesListInternal( @@ -278,7 +281,7 @@ class LightLayoutAttributes : public AbstractAttributes { /** * @brief Return how many lights are in this light layout - number of - * subconfigs in @ref lightInstConfig_ subconfig. + * @ref LightInstanceAttributes in @ref lightInstConfig_ subconfig. */ int getNumLightInstances() const { return this->getNumSubAttributesInternal("", lightInstConfig_); @@ -307,13 +310,13 @@ class LightLayoutAttributes : public AbstractAttributes { std::string getObjectInfoInternal() const override; /** - * @brief Smartpointer to created light instance configuration. The + * @brief Smartpointer to created @ref LightInstanceAttributes configuration. The * configuration is created on LightLayoutAttributes construction. */ std::shared_ptr lightInstConfig_{}; /** - * @brief Deque holding all released IDs to consume for light instances when + * @brief Deque holding all released IDs to consume for @ref LightInstanceAttributess when * one is deleted, before using size of lightInstances_ container. */ std::deque availableLightIDs_; diff --git a/src/esp/metadata/attributes/SemanticAttributes.h b/src/esp/metadata/attributes/SemanticAttributes.h index 809b6eac0b..36180e0296 100644 --- a/src/esp/metadata/attributes/SemanticAttributes.h +++ b/src/esp/metadata/attributes/SemanticAttributes.h @@ -132,7 +132,7 @@ class SemanticVolumeAttributes : public AbstractAttributes { */ class SemanticAttributes : public AbstractAttributes { public: - explicit SemanticAttributes(const std::string& handle); + explicit SemanticAttributes(const std::string& handle = ""); SemanticAttributes(const SemanticAttributes& otr); SemanticAttributes(SemanticAttributes&& otr) noexcept; @@ -356,16 +356,16 @@ class SemanticAttributes : public AbstractAttributes { } /** - * @brief Set whether or not the semantic asset for this stage supports - * texture semantics. + * @brief Set whether or not the semantic asset described by this attributes + * supports texture semantics. */ void setHasSemanticTextures(bool hasSemanticTextures) { set("has_semantic_textures", hasSemanticTextures); } /** - * @brief Get whether or not the semantic asset for this stage supports - * texture semantics. + * @brief Get whether or not the semantic asset described by this attributes + * supports texture semantics. */ bool getHasSemanticTextures() const { return get("has_semantic_textures"); diff --git a/src/esp/metadata/managers/SemanticAttributesManager.cpp b/src/esp/metadata/managers/SemanticAttributesManager.cpp index b00cac3b69..c09353b9c6 100644 --- a/src/esp/metadata/managers/SemanticAttributesManager.cpp +++ b/src/esp/metadata/managers/SemanticAttributesManager.cpp @@ -144,11 +144,32 @@ void SemanticAttributesManager::setValsFromJSONDoc( // directory location where semantic attributes files are found std::string semanticLocFileDir = semanticAttribs->getFileDirectory(); + // load semantic asset specific up orientation + io::jsonIntoConstSetter( + jsonConfig, "semantic_up", [semanticAttribs](const Magnum::Vector3& up) { + semanticAttribs->setSemanticOrientUp(up); + }); + + // load semantic asset specific front orientation + io::jsonIntoConstSetter( + jsonConfig, "semantic_front", + [semanticAttribs](const Magnum::Vector3& front) { + semanticAttribs->setSemanticOrientFront(front); + }); + + // load whether the semantic asset described has semantically annotated + // textures + io::jsonIntoSetter( + jsonConfig, "has_semantic_textures", + [semanticAttribs](bool has_semantic_textures) { + semanticAttribs->setHasSemanticTextures(has_semantic_textures); + }); + // Set the semantic asset filename std::string semanticAsset = ""; if (io::readMember(jsonConfig, "semantic_asset", semanticAsset)) { - // if "semantic mesh" is specified in stage json to non-empty value, set + // if "semantic mesh" is specified in source json to non-empty value, set // value (override default). // semantic asset filename might already be fully qualified; if // not, might just be file name @@ -163,7 +184,7 @@ void SemanticAttributesManager::setValsFromJSONDoc( std::string semanticSceneDescriptor = ""; if (io::readMember(jsonConfig, "semantic_descriptor_filename", semanticSceneDescriptor)) { - // if "semantic_descriptor_filename" is specified in stage json, set value + // if "semantic_descriptor_filename" is specified in source json, set value // (override default). // semanticSceneDescriptor filename might already be fully qualified; if // not, might just be file name