Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

--[Part 2 of 2] Semantic region building #2307

Merged
merged 28 commits into from
Jan 31, 2024
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
1704ba3
--add support for non-wildcard relative filenames.
jturner65 Jan 23, 2024
ab893d0
--pass semantic attributes to loadSemanticSceneDescriptor instead of …
jturner65 Jan 23, 2024
2c9621a
--move all semantic scene creation code to SemanticScene
jturner65 Jan 23, 2024
9210c44
--add semantic region creation and bindings.
jturner65 Jan 23, 2024
635e775
--fix mp3d test
jturner65 Jan 23, 2024
906a727
--clang-tidy
jturner65 Jan 23, 2024
45185d8
--add regions to the scene's region vector
jturner65 Jan 23, 2024
aa73975
viewer.py region rendering demo
aclegg3 Jan 23, 2024
9238d7e
fix viewer typo introduced by flaker
aclegg3 Jan 23, 2024
dd0a73c
--make extrusions
jturner65 Jan 25, 2024
59ff50c
--rename sub-configs to SemanticVolumes
jturner65 Jan 26, 2024
53563c5
--add construction of volume edges for debug/visualizations.
jturner65 Jan 26, 2024
eedb92b
--add semantic visualization in a more formal manner.
jturner65 Jan 26, 2024
f15dcf8
--initial test commit
jturner65 Jan 30, 2024
08d28c5
--minor message cleanup
jturner65 Jan 31, 2024
02d1340
--test containment; fix height calc and containment check.
jturner65 Jan 31, 2024
8a9ebea
--cleanup and expand c++ tests;
jturner65 Jan 31, 2024
b93ebc7
--update test to read from test JSON file instead of using a JSON str…
jturner65 Jan 31, 2024
bab9477
--clean up
jturner65 Jan 31, 2024
4414404
--ssd specified through scene dataset config default value
jturner65 Jan 31, 2024
8bc63e7
--add semantic config files with names matching scene dataset tags
jturner65 Jan 31, 2024
c032437
--update test dataset config
jturner65 Jan 31, 2024
d787c70
--require ssd handle to be found explicitly in dataset
jturner65 Jan 31, 2024
dc43e79
--adjust test to cover expanded test scene dataset.
jturner65 Jan 31, 2024
4e79bf0
--minor simplification of attributes managers tests.
jturner65 Jan 31, 2024
feccd0c
--add bindings specifically for loop-based semantic category
jturner65 Jan 31, 2024
6bfedef
--python test for semantic regions
jturner65 Jan 31, 2024
f49ced1
--fix attempting to map ssd file handle if scene instance is NONE scene.
jturner65 Jan 31, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 41 additions & 7 deletions examples/viewer.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@ def __init__(self, sim_settings: Dict[str, Any]) -> None:
self.debug_bullet_draw = False
# draw active contact point debug line visualizations
self.contact_debug_draw = False
# draw semantic region debug visualizations if present
self.semantic_region_debug_draw = False

# cache most recently loaded URDF file for quick-reload
self.cached_urdf = ""

Expand Down Expand Up @@ -176,6 +179,7 @@ def __init__(self, sim_settings: Dict[str, Any]) -> None:
self.replay_renderer_cfg: Optional[ReplayRendererConfiguration] = None
self.replay_renderer: Optional[ReplayRenderer] = None
self.reconfigure_sim()
self.debug_semantic_colors = {}

# compute NavMesh if not already loaded by the scene.
if (
Expand All @@ -189,40 +193,54 @@ def __init__(self, sim_settings: Dict[str, Any]) -> None:
logger.setLevel("INFO")
self.print_help_text()

def draw_contact_debug(self):
def draw_contact_debug(self, debug_line_render: Any):
"""
This method is called to render a debug line overlay displaying active contact points and normals.
Yellow lines show the contact distance along the normal and red lines show the contact normal at a fixed length.
"""
yellow = mn.Color4.yellow()
red = mn.Color4.red()
cps = self.sim.get_physics_contact_points()
self.sim.get_debug_line_render().set_line_width(1.5)
debug_line_render.set_line_width(1.5)
camera_position = self.render_camera.render_camera.node.absolute_translation
# only showing active contacts
active_contacts = (x for x in cps if x.is_active)
for cp in active_contacts:
# red shows the contact distance
self.sim.get_debug_line_render().draw_transformed_line(
debug_line_render.draw_transformed_line(
cp.position_on_b_in_ws,
cp.position_on_b_in_ws
+ cp.contact_normal_on_b_in_ws * -cp.contact_distance,
red,
)
# yellow shows the contact normal at a fixed length for visualization
self.sim.get_debug_line_render().draw_transformed_line(
debug_line_render.draw_transformed_line(
cp.position_on_b_in_ws,
# + cp.contact_normal_on_b_in_ws * cp.contact_distance,
cp.position_on_b_in_ws + cp.contact_normal_on_b_in_ws * 0.1,
yellow,
)
self.sim.get_debug_line_render().draw_circle(
debug_line_render.draw_circle(
translation=cp.position_on_b_in_ws,
radius=0.005,
color=yellow,
normal=camera_position - cp.position_on_b_in_ws,
)

def draw_region_debug(self, debug_line_render: Any) -> None:
"""
Draw the semantic region wireframes.
"""

for region in self.sim.semantic_scene.regions:
color = self.debug_semantic_colors.get(region.id, mn.Color4.magenta())
for edge in region.volume_edges:
debug_line_render.draw_transformed_line(
edge[0],
edge[1],
color,
)

def debug_draw(self):
"""
Additional draw commands to be called during draw_event.
Expand All @@ -231,8 +249,18 @@ def debug_draw(self):
render_cam = self.render_camera.render_camera
proj_mat = render_cam.projection_matrix.__matmul__(render_cam.camera_matrix)
self.sim.physics_debug_draw(proj_mat)

debug_line_render = self.sim.get_debug_line_render()
if self.contact_debug_draw:
self.draw_contact_debug()
self.draw_contact_debug(debug_line_render)

if self.semantic_region_debug_draw:
if len(self.debug_semantic_colors) != len(self.sim.semantic_scene.regions):
for region in self.sim.semantic_scene.regions:
self.debug_semantic_colors[region.id] = mn.Color4(
mn.Vector3(np.random.random(3))
)
self.draw_region_debug(debug_line_render)

def draw_event(
self,
Expand Down Expand Up @@ -473,6 +501,12 @@ def key_press_event(self, event: Application.KeyEvent) -> None:

elif key == pressed.H:
self.print_help_text()
elif key == pressed.J:
logger.info(
f"Toggle Region Draw from {self.semantic_region_debug_draw } to {not self.semantic_region_debug_draw}"
)
# Toggle visualize semantic bboxes. Currently only regions supported
self.semantic_region_debug_draw = not self.semantic_region_debug_draw

elif key == pressed.TAB:
# NOTE: (+ALT) - reconfigure without cycling scenes
Expand Down Expand Up @@ -590,7 +624,6 @@ def key_press_event(self, event: Application.KeyEvent) -> None:
elif key == pressed.V:
self.invert_gravity()
logger.info("Command: gravity inverted")

elif key == pressed.N:
# (default) - toggle navmesh visualization
# NOTE: (+ALT) - re-sample the agent position on the NavMesh
Expand Down Expand Up @@ -973,6 +1006,7 @@ def print_help_text(self) -> None:
',': Render a Bullet collision shape debug wireframe overlay (white=active, green=sleeping, blue=wants sleeping, red=can't sleep).
'c': Run a discrete collision detection pass and render a debug wireframe overlay showing active contact points and normals (yellow=fixed length normals, red=collision distances).
(+SHIFT) Toggle the contact point debug render overlay on/off.
'j' Toggle Semantic visualization bounds (currently only Semantic Region annotations)

Object Interactions:
SPACE: Toggle physics simulation on/off.
Expand Down
85 changes: 19 additions & 66 deletions src/esp/assets/ResourceManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -305,77 +305,30 @@ std::vector<std::string> ResourceManager::buildVertexColorMapReport(
semanticScene_);
} // ResourceManager::buildVertexColorMapReport

bool ResourceManager::loadSemanticSceneDescriptor(
const std::string& ssdFilename,
bool ResourceManager::loadSemanticScene(
const std::shared_ptr<metadata::attributes::SemanticAttributes>&
semanticAttr,
const std::string& activeSceneName) {
namespace FileUtil = Cr::Utility::Path;
const std::string ssdFilename =
semanticAttr != nullptr ? semanticAttr->getSemanticDescriptorFilename()
: "";
semanticScene_ = nullptr;
if (ssdFilename != "") {
if ((ssdFilename != "") || ((semanticAttr != nullptr) &&
(semanticAttr->getNumRegionInstances() > 0))) {
bool success = false;
// semantic scene descriptor might not exist
// semantic scene descriptor might not exist so (re)create it.
semanticScene_ = scene::SemanticScene::create();
ESP_DEBUG(Mn::Debug::Flag::NoSpace)
<< "SceneInstance : `" << activeSceneName
<< "` proposed Semantic Scene Descriptor filename : `" << ssdFilename
<< "`.";

bool fileExists = FileUtil::exists(ssdFilename);
if (fileExists) {
// Attempt to load semantic scene descriptor specified in scene instance
// file, agnostic to file type inferred by name, if file exists.
success = scene::SemanticScene::loadSemanticSceneDescriptor(
ssdFilename, *semanticScene_);
if (success) {
ESP_DEBUG(Mn::Debug::Flag::NoSpace)
<< "SSD with SceneInstanceAttributes-provided name `" << ssdFilename
<< "` successfully found and loaded.";
} else {
// here if provided file exists but does not correspond to appropriate
// SSD
ESP_ERROR(Mn::Debug::Flag::NoSpace)
<< "SSD Load Failure! File with "
"SceneInstanceAttributes-provided name `"
<< ssdFilename << "` exists but failed to load.";
}
return success;
// if not success then try to construct a name
} else {
// attempt to look for specified file failed, attempt to build new file
// name by searching in path specified of specified file for
// info_semantic.json file for replica dataset
const std::string constructedFilename = FileUtil::join(
FileUtil::split(ssdFilename).first(), "info_semantic.json");
fileExists = FileUtil::exists(constructedFilename);
if (fileExists) {
success = scene::SemanticScene::loadReplicaHouse(constructedFilename,
*semanticScene_);
if (success) {
ESP_DEBUG(Mn::Debug::Flag::NoSpace)
<< "SSD for Replica using constructed file : `"
<< constructedFilename << "` in directory with `" << ssdFilename
<< "` loaded successfully";
} else {
// here if constructed file exists but does not correspond to
// appropriate SSD or some loading error occurred.
ESP_ERROR(Mn::Debug::Flag::NoSpace)
<< "SSD Load Failure! Replica file with constructed name `"
<< ssdFilename << "` exists but failed to load.";
}
return success;
} else {
// neither provided non-empty filename nor constructed filename
// exists. This is probably due to an incorrect naming in the
// SceneInstanceAttributes
ESP_WARNING(Mn::Debug::Flag::NoSpace)
<< "SSD File Naming Issue! Neither "
"SceneInstanceAttributes-provided name : `"
<< ssdFilename << "` nor constructed filename : `"
<< constructedFilename << "` exist on disk.";
return false;
}
} // if given SSD file name specified exists
} // if semantic scene descriptor specified in scene instance

success = scene::SemanticScene::loadSemanticSceneDescriptor(
semanticAttr, *semanticScene_);

ESP_VERY_VERBOSE(Mn::Debug::Flag::NoSpace)
<< "Attempt to create SemanticScene for Current Scene :`"
<< activeSceneName << "` with ssdFilename : `" << ssdFilename
<< "` :" << (success ? " " : "Not ") << "Successful";

return success;
}
return false;
} // ResourceManager::loadSemanticSceneDescriptor

Expand Down
15 changes: 10 additions & 5 deletions src/esp/assets/ResourceManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ namespace attributes {
class ObjectAttributes;
class PbrShaderAttributes;
class PhysicsManagerAttributes;
class SemanticAttributes;
class SceneObjectInstanceAttributes;
class StageAttributes;
} // namespace attributes
Expand Down Expand Up @@ -193,15 +194,19 @@ class ResourceManager {
bool semanticSceneExists() const { return (semanticScene_ != nullptr); }

/**
* @brief Load semantic scene descriptor file specified by @p ssdFilename ,
* for the passed @p activeSceneName .
* @param ssdFilename The fully qualified filename candidate for the ssd file.
* @brief Load semantic scene data from descriptor file and metadata specified
* in @p semanticAttr , for the passed @p activeSceneName .
* @param semanticAttr Pointer to semantic attributes, if they exist. This
* will hold fully-qualified filename along with other attributes required to
* create the semantic scene.
* @param activeSceneName Name of the currently active scene that we will be
* loading the SSD for.
* @return whether loaded successfully or not.
*/
bool loadSemanticSceneDescriptor(const std::string& ssdFilename,
const std::string& activeSceneName);
bool loadSemanticScene(
const std::shared_ptr<metadata::attributes::SemanticAttributes>&
semanticAttr,
const std::string& activeSceneName);

/**
* @brief Load a scene mesh and add it to the specified @ref DrawableGroup as
Expand Down
21 changes: 19 additions & 2 deletions src/esp/bindings/SceneBindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -159,13 +159,30 @@ void initSceneBindings(py::module& m) {
semanticRegion
.def_property_readonly(
"id", &SemanticRegion::id,
"The ID of the region, of the form ``<level_id>_<region_id>``")
"The ID of the region, either as the region's unique name, or of the "
"form ``<level_id>_<region_id>``")
.def_property_readonly("level", &SemanticRegion::level)
.def_property_readonly("aabb", &SemanticRegion::aabb)
.def_property_readonly("category", &SemanticRegion::category,
"The semantic category of the region")
.def_property_readonly("objects", &SemanticRegion::objects,
"All objects in the region");
"All objects in the region")
.def_property_readonly("poly_loop_points",
&SemanticRegion::getPolyLoopPoints,
"The points making up the polyloop for this "
"region, coplanar and parallel to the floor.")
.def_property_readonly(
"volume_edges", &SemanticRegion::getVisEdges,
"The edges, as pairs of points, that determine "
"the boundaries of the region. For visualizations.")
.def_property_readonly("floor_height", &SemanticRegion::getFloorHeight,
"The height above the x-z plane for the floor of "
"the semantic region.")
.def_property_readonly("extrusion_height",
&SemanticRegion::getExtrusionHeight,
"The height of the extrusion above the floor.")
.def("contains", &SemanticRegion::contains, "point"_a,
"Check whether the given point is contained in the given region.");

// ==== SemanticObject ====
semanticObject
Expand Down
24 changes: 12 additions & 12 deletions src/esp/metadata/attributes/SemanticAttributes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,17 @@ namespace metadata {
namespace attributes {

/////////////////////////////////
// SemanticRegionAttributes
// SemanticVolumeAttributes

SemanticRegionAttributes::SemanticRegionAttributes(const std::string& handle)
: AbstractAttributes("SemanticRegionAttributes", handle) {
SemanticVolumeAttributes::SemanticVolumeAttributes(const std::string& handle)
: AbstractAttributes("SemanticVolumeAttributes", handle) {
// Initialize values
set("floor_height", 0.0);
set("extrusion_height", 2.5);

} // SemanticRegionAttributes ctor
} // SemanticVolumeAttributes ctor

std::string SemanticRegionAttributes::getObjectInfoHeaderInternal() const {
std::string SemanticVolumeAttributes::getObjectInfoHeaderInternal() const {
std::string res =
"Name,Label,Floor Height,Extrusion Height,Min Bounds,Max Bounds,";
int iter = 0;
Expand All @@ -29,7 +29,7 @@ std::string SemanticRegionAttributes::getObjectInfoHeaderInternal() const {
return res;
}

std::string SemanticRegionAttributes::getObjectInfoInternal() const {
std::string SemanticVolumeAttributes::getObjectInfoInternal() const {
std::string res = Cr::Utility::formatString(
"{},{},{},{},{},{},", getAsString("handle"), getAsString("label"),
getAsString("floor_height"), getAsString("extrusion_height"),
Expand All @@ -42,12 +42,12 @@ std::string SemanticRegionAttributes::getObjectInfoInternal() const {
Cr::Utility::formatInto(res, res.size(), "],");

return res;
} // SemanticRegionAttributes::getObjectInfoInternal()
} // SemanticVolumeAttributes::getObjectInfoInternal()

void SemanticRegionAttributes::writeValuesToJson(
void SemanticVolumeAttributes::writeValuesToJson(
io::JsonGenericValue& jsonObj,
io::JsonAllocator& allocator) const {
// map "handle" to "name" key in json - this SemanticRegionAttributes' handle
// map "handle" to "name" key in json - this SemanticVolumeAttributes' handle
// is its unique name
writeValueToJson("handle", "name", jsonObj, allocator);
writeValueToJson("label", jsonObj, allocator);
Expand All @@ -60,7 +60,7 @@ void SemanticRegionAttributes::writeValuesToJson(
io::addMember(jsonObj, "poly_loop", polyLoop_, allocator);
}

} // SemanticRegionAttributes::writeValuesToJson
} // SemanticVolumeAttributes::writeValuesToJson

/////////////////////////////////
// SemanticAttributes
Expand All @@ -85,7 +85,7 @@ SemanticAttributes::SemanticAttributes(const SemanticAttributes& otr)
availableRegionInstIDs_(otr.availableRegionInstIDs_) {
// get refs to internal subconfigs for semantic region attributes
regionAnnotationConfig_ = editSubconfig<Configuration>("region_annotations");
copySubconfigIntoMe<SemanticRegionAttributes>(otr.regionAnnotationConfig_,
copySubconfigIntoMe<SemanticVolumeAttributes>(otr.regionAnnotationConfig_,
regionAnnotationConfig_);
} // SemanticAttributes copy ctor

Expand Down Expand Up @@ -162,7 +162,7 @@ SemanticAttributes& SemanticAttributes::operator=(
// get refs to internal subconfigs for semantic region attributes
regionAnnotationConfig_ =
editSubconfig<Configuration>("region_annotations");
copySubconfigIntoMe<SemanticRegionAttributes>(otr.regionAnnotationConfig_,
copySubconfigIntoMe<SemanticVolumeAttributes>(otr.regionAnnotationConfig_,
regionAnnotationConfig_);
}
return *this;
Expand Down
Loading