def emit_pointcloud(app, options, pointloud_name, usd_tfm, visibility, usd_prim, xsi_parent): imp.reload(utils) usd_points = UsdGeom.Points(usd_prim) xsi_points = app.GetPrim("PointCloud", pointloud_name, xsi_parent) utils.set_xsi_transform(app, xsi_points, usd_tfm) utils.set_xsi_visibility(xsi_points, visibility) tree = create_usd_load_tree(app, xsi_points.ActivePrimitive.Geometry) if not utils.is_animated_points(usd_points): points_data = {} read_points_data(points_data, usd_points=usd_points) xsi_geometry = xsi_points.ActivePrimitive.Geometry set_pointcloud_from_data(app, xsi_geometry, points_data, options["XSIMath"]) else: operator = app.AddCustomOp("USDPointsOperator", xsi_points.ActivePrimitive, "", "USDPointsOperator") operator.Parameters("file_path").Value = options["file_path"] operator.Parameters("points_path").Value = str(usd_prim.GetPath()) operator.AlwaysEvaluate = True # swap ICE-tree and operator (ICE-tree should be at the top) app.MoveOperatorAfter(xsi_points.ActivePrimitive, tree, operator) return xsi_points
def test_ComputePointCount(self): stage = Usd.Stage.Open('points.usda') unset = UsdGeom.Points.Get(stage, '/UnsetPoints') blocked = UsdGeom.Points.Get(stage, '/BlockedPoints') empty = UsdGeom.Points.Get(stage, '/EmptyPoints') timeSampled = UsdGeom.Points.Get(stage, '/TimeSampledPoints') timeSampledAndDefault = UsdGeom.Points.Get( stage, '/TimeSampledAndDefaultPoints') testTimeSamples = [(unset, Usd.TimeCode.EarliestTime(), 0), (blocked, Usd.TimeCode.EarliestTime(), 0), (empty, Usd.TimeCode.EarliestTime(), 0), (timeSampled, Usd.TimeCode.EarliestTime(), 3), (timeSampledAndDefault, Usd.TimeCode.EarliestTime(), 5)] testDefaults = [(unset, 0), (blocked, 0), (empty, 0), (timeSampled, 0), (timeSampledAndDefault, 4)] for (schema, timeCode, expected) in testTimeSamples: self.assertTrue(schema) self.assertEqual(schema.GetPointCount(timeCode), expected) for (schema, expected) in testDefaults: self.assertTrue(schema) self.assertEqual(schema.GetPointCount(), expected) invalid = UsdGeom.Points(Usd.Prim()) self.assertFalse(invalid) with self.assertRaises(RuntimeError): self.assertEqual(invalid.GetPointCount(), 0) with self.assertRaises(RuntimeError): self.assertEqual( invalid.GetPointCount(Usd.TimeCode.EarliestTime()), 0)
def read_points_data(data_dict, file_path=None, points_path=None, usd_points=None): if usd_points is None: stage = Usd.Stage.Open(file_path) usd_points = UsdGeom.Points(stage.GetPrimAtPath(points_path)) data_dict["points"] = read_points(usd_points) data_dict["widths"] = read_widths(usd_points)
def add_pointcloud(stage, points, scene_path, colors=None, time=None): r"""Add a pointcloud to an existing USD stage. Create a pointcloud represented by point instances of a sphere centered at each point coordinate. The stage is modified but not saved to disk. Args: stage (Usd.Stage): Stage onto which to add the pointcloud. points (torch.FloatTensor): Pointcloud tensor containing ``N`` points of shape ``(N, 3)``. scene_path (str): Absolute path of pointcloud within the USD file scene. Must be a valid Sdf.Path. colors (torch.FloatTensor, optional): Color tensor corresponding each point in the pointcloud tensor of shape ``(N, 3)``. time (int, optional): Positive integer defining the time at which the supplied parameters correspond to. Returns: (Usd.Stage) Example: >>> stage = create_stage('./new_stage.usd') >>> points = torch.rand(100, 3) >>> stage = add_pointcloud(stage, points, '/World/PointClouds/pointcloud_0') >>> stage.Save() """ scene_path = Sdf.Path(scene_path) if time is None: time = Usd.TimeCode.Default() if stage.GetPrimAtPath(scene_path): points_prim = stage.GetPrimAtPath(scene_path) else: points_prim = stage.DefinePrim(scene_path, 'Points') geom_points = UsdGeom.Points(points_prim) # Calculate default point scale bounds = points.max(dim=0)[0] - points.min(dim=0)[0] min_bound = min(bounds) scale = (min_bound / points.size(0) ** (1 / 3)).item() # Generate instancer parameters positions = points.cpu().tolist() scales = [scale, ] * points.size(0) # Populate UsdGeomPoints geom_points.GetPointsAttr().Set(points.numpy(), time=time) geom_points.GetWidthsAttr().Set(Vt.FloatArray(scales), time=time) if colors is not None: assert colors.shape == points.shape, "Colors and points must have the same shape." geom_points.GetDisplayColorAttr().Set(colors.numpy(), time=time) return stage
def get_pointcloud_bracketing_time_samples(stage, scene_path, target_time): """Returns two time samples that bracket target_time for point cloud attributes at a specified scene_path. Args: stage (Usd.Stage) scene_path (str) target_time (Number) Returns: (iterable of 2 numbers) """ # Note: can also get usd_attr.GetTimeSamples() prim = stage.GetPrimAtPath(scene_path) if UsdGeom.Points(prim): geom_points = UsdGeom.Points(prim) result = geom_points.GetPointsAttr().GetBracketingTimeSamples(target_time) elif UsdGeom.PointInstancer(prim): instancer = UsdGeom.PointInstancer(prim) result = instancer.GetPositionsAttr().GetBracketingTimeSamples(target_time) else: raise TypeError("The prim is neither UsdGeomPoints nor UsdGeomPointInstancer.") return result
def emit_pointcloud(app, options, pointloud_name, usd_tfm, visibility, usd_prim, is_strands, xsi_parent, is_simple=False): '''if is_simple is True, then we should ignore in-object ransform ''' if DEBUG_MODE: imp.reload(materials) imp.reload(utils) usd_object = UsdGeom.BasisCurves( usd_prim) if is_strands else UsdGeom.Points(usd_prim) xsi_points = app.GetPrim("PointCloud", pointloud_name, xsi_parent) if options.get("is_materials", False): usd_material = UsdShade.MaterialBindingAPI( usd_prim).GetDirectBinding().GetMaterial() xsi_material = materials.import_material( app, usd_material, library_name=options["file_name"]) if xsi_material is not None: app.AssignMaterial(xsi_material.FullName + "," + xsi_points.FullName) utils.set_xsi_transform(app, xsi_points, usd_tfm, up_key=options["up_axis"]) utils.set_xsi_visibility(xsi_points, visibility) if "project_path" in options: is_constant = write_ice_cache(usd_object, is_strands, xsi_points, options["project_path"], options["file_name"], options["up_axis"], is_simple) # build ice-tree with caching node build_ice_tree(app, xsi_points, is_constant, options["file_name"]) return xsi_points
def test_Points(self): stage = Usd.Stage.Open("testPoints.usda") points = UsdGeom.Points(stage.GetPrimAtPath("/Points")) # Verify the extent computation when no transform matrix is given. self.verifyExtent(points, [(-0.5, -0.5, -0.5), (3.25, 2.0, 2.25)]) # Verify the extent computation when no transform matrix is given. self.verifyExtent(points, [(-0.5, -0.5, -0.5), (3.25, 2.0, 2.25)], Gf.Matrix4d(1.0)) # Apply an arbitrary transform matrix. Note that this is different from # the extent displayed in Usdview. If you open testPoints.usda and # select /StrangePoints, you will see the box is larger than it needs to # be (it transforms the bounding box around the points, not the points # themselves like ComputeExtentFromPlugins). If you select # /StrangePointsComputedExtent, it will display the (much tighter) # computed extent used below. self.verifyExtent( points, [(0.18938279151916504, 1.189382791519165, 1.0946913957595825), (4.8312811851501465, 3.609475612640381, 2.041466236114502)], STRANGE_TRANSFORM)
def import_pointclouds(file_path, scene_paths=None, times=None): r"""Import one or more pointclouds from a USD file. Assumes that pointclouds are interpreted using point instancers or UsdGeomPoints. Converts the coordinates of each point instance to a point within the output pointcloud. Args: file_path (str): Path to usd file (\*.usd, \*.usda). scene_paths (list of str, optional): Scene path(s) within the USD file indicating which primitive(s) to import. If None, will return all pointclouds found based on PointInstancer or UsdGeomPoints prims with `kaolin_type` primvar set to `PointCloud`. times (list of int): Positive integers indicating the time at which to retrieve parameters. Returns: list of namedtuple of: - **points** (list of torch.FloatTensor): of shape (num_points, 3) - **colors** (list of torch.FloatTensor): of shape (num_points, 3) - **normals** (list of torch.FloatTensor): of shape (num_points, 2) Example: >>> points = torch.rand(100, 3) >>> stage = export_pointclouds('./new_stage.usd', [points, points, points]) >>> pointclouds = import_pointclouds(file_path='./new_stage.usd')[0] >>> len(pointclouds) 3 >>> pointclouds[0].shape torch.Size([100, 3]) """ assert os.path.exists(file_path) if scene_paths is None: scene_paths = get_pointcloud_scene_paths(file_path) if times is None: times = [Usd.TimeCode.Default()] * len(scene_paths) pointclouds = [] colors = [] normals = [] stage = Usd.Stage.Open(file_path) for scene_path, time in zip(scene_paths, times): prim = stage.GetPrimAtPath(scene_path) assert prim, f'The prim at {scene_path} does not exist.' if UsdGeom.Points(prim): geom_points = UsdGeom.Points(prim) pointclouds.append(torch.tensor(geom_points.GetPointsAttr().Get(time=time))) color = geom_points.GetDisplayColorAttr().Get(time=time) if color is None: colors.append(color) else: colors.append(torch.tensor(color)) elif UsdGeom.PointInstancer(prim): instancer = UsdGeom.PointInstancer(prim) pointclouds.append(torch.tensor(instancer.GetPositionsAttr().Get(time=time))) colors.append(None) else: raise TypeError("The prim is neither UsdGeomPoints nor UsdGeomPointInstancer.") # TODO: place holders for normals for now normals = [None] * len(colors) params = [pointclouds, colors, normals] return [pointcloud_return_type(p, c, n) for p, c, n in zip(*params)]