# How to duplicate a prim?

**URL:** <https://forum.aousd.org/t/how-to-duplicate-a-prim/1008>\
**Category:** USD\
**Tags:** cpp, beginner\
**Created:** [December 1, 2023, 3:08pm UTC](https://forum.aousd.org/t/how-to-duplicate-a-prim/1008 "2023-12-01T15:08:43Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![zdarova](https://sea2.discourse-cdn.com/flex016/user_avatar/forum.aousd.org/zdarova/32/345_2.png) [@zdarova](https://forum.aousd.org/u/zdarova)\
**Post date:** [December 1, 2023, 3:08pm UTC](https://forum.aousd.org/t/how-to-duplicate-a-prim/1008/1 "2023-12-01T15:08:43Z")

</div>

I just want to duplicate any specific prim via C++ USD API.

In omniverse CREATE I’ve created an empty scene, dropped local asset  
I’ve saved the scene as a .usda to look what I have. Verified that prim is here:

```auto
def "Cardbox_A3_01" (
        instanceable = true
        prepend payload = @file:/C:/Users/hellothere/Downloads/ov-industrial3dpack-01-100.1.3/Assets/ArchVis/Industrial/Containers/Cardboard/Cardbox_A3.usd@
    )
    {
        float3 xformOp:rotateXYZ = (0, -90, -90)
        float3 xformOp:scale = (1, 1, 1)
        double3 xformOp:translate = (60.27730765253733, -8.036520471939059e-29, -5.649315969397589)
        uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:rotateXYZ", "xformOp:scale"]
    }

```

I want my app to duplicate that prim (only a name should be different), after a 3 days i finally got something very close to my goal. My edited .usda now contains extra prim:

```auto
def Xform "Cardbox_A3_09" (
        instanceable = true
        kind = "component"
    )
    {
        float3 xformOp:rotateXYZ = (0, -90, -90)
        float3 xformOp:scale = (1, 1, 1)
        double3 xformOp:translate = (60.27730765253733, -8.036520471939059e-29, -5.649315969397589)
        uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:rotateXYZ", "xformOp:scale"]
    }

```

As you can notice, there is still some difference:

1. redundant line _kind = “component”_
2. there is no _prepend payload = @…_

What should I correct in my code to get exactly same prim (except name)?  
Is there some _simple_ way to clone a prim and add it into stage?

```auto
static void copyMeta(UsdObject origin, UsdObject target) {
    UsdMetadataValueMap mm = origin.GetAllAuthoredMetadata();

    for (auto& meta : mm) {
        target.SetMetadata(meta.first, meta.second);
    }
}

static void copyProp(UsdProperty pr, UsdPrim target) {
    UsdProperty newProp;
    if (UsdAttribute attr = pr.As<UsdAttribute>()) {
        UsdAttribute newAttr = target.CreateAttribute(attr.GetName(), attr.GetTypeName());
        VtValue val = VtValue();
        VtValue* valPtr = &val;
        if (attr.Get(valPtr)) {
            newAttr.Set(val);
        }
            
        newProp = newAttr;
        //TODO: copy animation
        //TODO: copy connections

    }
    else if (UsdRelationship rel = pr.As<UsdRelationship>()) {
        UsdRelationship newRel = target.CreateRelationship(rel.GetName(), rel.IsCustom());
        SdfPathVector pVec = SdfPathVector();
        SdfPathVector* vecPtr = &pVec;
        if (rel.GetTargets(vecPtr)) {
            newRel.SetTargets(pVec);
        }
        newProp = newRel;
        //TODO: set targets to newRel
    }
    if (newProp) {
        copyMeta(pr, newProp);
    }
    
}

static void clonePrim(UsdStageRefPtr stage, UsdPrim origin, std::string name) {
    SdfPath originPath = origin.GetPath().GetParentPath();
    //originPath = originPath;
    SdfPath newPath = SdfPath(originPath.GetAsString() + "/" + name);

    //TODO: if already exists?
    UsdPrim newPrim = stage->DefinePrim(newPath, origin.GetTypeName());

    copyMeta(origin, newPrim);

    for (UsdProperty pr : origin.GetAuthoredProperties()) {
        copyProp(pr, newPrim);
    }

    for (UsdPrim ch : origin.GetChildren()) {
        //TODO: copy child
        std::cout << "there is child" << std::endl;
    }

    //UsdPrimCompositionQuery
    UsdReferences refs = origin.GetPayloads();

}

```

---

<div class="post-metadata">

**Author:** ![BigRoyNL](https://sea2.discourse-cdn.com/flex016/user_avatar/forum.aousd.org/bigroynl/32/413_2.png) [@BigRoyNL](https://forum.aousd.org/u/BigRoyNL)\
**Post date:** [December 1, 2023, 4:16pm UTC](https://forum.aousd.org/t/how-to-duplicate-a-prim/1008/2 "2023-12-01T16:16:50Z")

</div>

> [@zdarova](#):
>
> Is there some _simple_ way to clone a prim and add it into stage?

I think [`Sdf.CopySpec`](https://openusd.org/dev/api/copy_utils_8h.html#ad20defdb6654ee0937942116177fd4cc) is the closest to this. However you’d be copying the individual prim specs from one layer to another (or same target layer but different name).

* * *

I’m not entirely sure if there’s any trivial way to clone a composed primitive state (that might have been defined and composed over multiple layers) and then copy that spec as a single contribution on a single layer.

---

<div class="post-metadata">

**Author:** ![zdarova](https://sea2.discourse-cdn.com/flex016/user_avatar/forum.aousd.org/zdarova/32/345_2.png) [@zdarova](https://forum.aousd.org/u/zdarova)\
**Post date:** [December 1, 2023, 4:23pm UTC](https://forum.aousd.org/t/how-to-duplicate-a-prim/1008/3 "2023-12-01T16:23:35Z")

</div>

Thank you, I will investigate Sdf.CopySpec.

Maybe I should go another way for duplication of assets on a scene? The goal is to control a _population_ of prims: quantity, coordinates of each etc.

---

<div class="post-metadata">

**Author:** ![BigRoyNL](https://sea2.discourse-cdn.com/flex016/user_avatar/forum.aousd.org/bigroynl/32/413_2.png) [@BigRoyNL](https://forum.aousd.org/u/BigRoyNL)\
**Post date:** [December 1, 2023, 4:28pm UTC](https://forum.aousd.org/t/how-to-duplicate-a-prim/1008/4 "2023-12-01T16:28:26Z")

</div>

There’s a recommendation [here](https://lucascheller.github.io/VFX-UsdSurvivalGuide/production/concepts.html#copying-data-with-sdfcopyspec) to maybe also look into `UsdUtils.StitchLayers`:

> [Copying specs (prim and properties) from layer to layer with Sdf.CopySpec()](https://lucascheller.github.io/VFX-UsdSurvivalGuide/production/concepts.html#copying-specs-prim-and-properties-from-layer-to-layer-with-sdfcopyspec)
> 
> The `Sdf.CopySpec` can copy anything that is representable via the `Sdf.Path`. This means we can copy prim/property/variant specs. When copying, the default is to completely replace the target spec.
> 
> We can filter this by passing in filter functions. Another option is to copy the content to a new anonymous layer and then merge it via `UsdUtils.StitchLayers(<StrongLayer>, <WeakerLayer>)`. This is often more “user friendly” than implementing a custom merge logic, as we get the “high layer wins” logic for free and this is what we are used to when working with USD.

Probably won’t be “optimal” in performance however.

---

<div class="post-metadata">

**Author:** ![BigRoyNL](https://sea2.discourse-cdn.com/flex016/user_avatar/forum.aousd.org/bigroynl/32/413_2.png) [@BigRoyNL](https://forum.aousd.org/u/BigRoyNL)\
**Post date:** [December 2, 2023, 11:43am UTC](https://forum.aousd.org/t/how-to-duplicate-a-prim/1008/5 "2023-12-02T11:43:37Z")

</div>

Here’s an example in Python:

```python
from pxr import Sdf, Usd

def unique_name(stage: Usd.Stage, prim_path: Sdf.Path) -> Sdf.Path:
    """Return Sdf.Path that is unique under the current composed stage.

    Note that this technically does not ensure that the Sdf.Path does not
    exist in any of the layers, e.g. it could be defined within a currently
    unselected variant or a muted layer.

    """
    src = prim_path.pathString.rstrip("123456789")
    i = 1
    while stage.GetPrimAtPath(prim_path):
        prim_path = Sdf.Path(f"{src}{i}")
        i += 1
    return prim_path

def duplicate_prim(prim: Usd.Prim) -> Sdf.Path:
    """Duplicate prim"""
    path = prim.GetPath()
    stage = prim.GetStage()
    new_path = unique_name(stage, path)
    for spec in prim.GetPrimStack():
        layer = spec.layer
        Sdf.CopySpec(layer, path, layer, new_path)        
    return new_path

```

Example usage:

```python
stage = Usd.Stage.CreateInMemory()
prim_a = stage.DefinePrim("/root/a", "Xform")
prim_b = stage.DefinePrim("/root/b", "Xform")
new_prim_a_path = duplicate_prim(prim_a)
print(new_prim_a_path)
# "/root/a1"
new_prim_b_path = duplicate_prim(prim_b)
print(new_prim_b_path)
# "/root/b1"

```

Note that this will clone the prim across **all** layers it is found _currently in the composed stage_ and then also create the duplicated specs in each of those layers. As such, it does not rely on or target the current edit target.

How one instead would clone a “composed prim” and then create those opinions only in the current stage’s edit target I don’t know. An example of that would be welcomed! 🙂

---

<div class="post-metadata">

**Author:** ![BigRoyNL](https://sea2.discourse-cdn.com/flex016/user_avatar/forum.aousd.org/bigroynl/32/413_2.png) [@BigRoyNL](https://forum.aousd.org/u/BigRoyNL)\
**Post date:** [December 2, 2023, 2:48pm UTC](https://forum.aousd.org/t/how-to-duplicate-a-prim/1008/6 "2023-12-02T14:48:20Z")

</div>

And here’s a quick prototype example code to duplicate a prim, flatten its opinions using `UsdUtils` stitch commands and then put that flattened prim spec into the new layer.

### Prototype 1: Duplicate prim with UsdUtils.StitchInfo

In this case I use `UsdUtils.StitchInfo` to solely merge specs however there are some caveats to be aware of:

- This does not stitch any childrens and their opinions, even though the initial call to `CopySpec` will copy over children prims from that stack. You might want to either remove all children from the copied spec or do something different there so you also transfer children and apply their stitched specs
- Unfortunately this requires a temporary layer because the logic of the `StitchInfo` checks whether the spec paths are equal - which I suppose is solely a “safety check” but due to that we can’t stitch into a duplicated prim that will of course require its own unique name.

```python
from pxr import Sdf, Usd, UsdUtils, UsdGeom

def unique_name(stage: Usd.Stage, prim_path: Sdf.Path) -> Sdf.Path:
    """Return Sdf.Path that is unique under the current composed stage.

    Note that this technically does not ensure that the Sdf.Path does not
    exist in any of the layers, e.g. it could be defined within a currently
    unselected variant or a muted layer.

    """
    src = prim_path.pathString.rstrip("123456789")
    i = 1
    while stage.GetPrimAtPath(prim_path):
        prim_path = Sdf.Path(f"{src}{i}")
        i += 1
    return prim_path

def duplicate_prim(prim: Usd.Prim) -> list[Sdf.Path]:
    """Duplicate prim flattened into current edit target"""
    path = prim.GetPath()
    stage = prim.GetStage()
    
    stack = prim.GetPrimStack()
    initial_spec = stack.pop()
    
    # Copy spec to a temp layer where we use stitch utiltiies
    # to merge the opinions of multiple layers of the original
    # prim's PrimStack so we have a 'merged opinion' on one
    # layer
    tmp_layer = Sdf.Layer.CreateAnonymous()
    Sdf.CreatePrimInLayer(tmp_layer, path.GetParentPath()) # ensure parent exists
    if not Sdf.CopySpec(initial_spec.layer, path, tmp_layer, path):
        raise RuntimeError("No spec copied")
        
    # Add the additional opinions that make the original prim stack
    new_spec = tmp_layer.GetPrimAtPath(path)
    for spec in stack:
        # Note that this does not copy over opinions to prim specs
        # nor does it copy over children. It doesn't seem UsdUtils.StitchValueFn
        # is exposed to Python so we also couldn't make it copy over new children
        # with a custom call to `Sdf.CopySpec`
        UsdUtils.StitchInfo(new_spec, spec) 
    
    # Now copy the flattened spec to the target "duplicate"
    target = stage.GetEditTarget()
    target_layer = target.GetLayer()
    new_path = unique_name(stage, path)
    new_path = target.MapToSpecPath(new_path)
    Sdf.CopySpec(tmp_layer, path, target_layer, new_path)
    
    return new_path

# Create a stage with an Xform prim
stage = Usd.Stage.CreateInMemory()
prim = stage.DefinePrim("/root/a", "Xform")

# Now add an opinion in another layer
layer = Sdf.Layer.CreateAnonymous()
prim_spec = Sdf.CreatePrimInLayer(layer, "/root/a")
attr_spec = Sdf.AttributeSpec(prim_spec, "my_custom_attr", Sdf.ValueTypeNames.Double)
attr_spec.default = 10
attr_spec.interpolation = UsdGeom.Tokens.constant

# Sublayer the path ontot the stage
root_layer = stage.GetRootLayer()
root_layer.subLayerPaths.append(layer.identifier)

# Debugging: Make sure the attribute appears on the composed stage
prim = stage.GetPrimAtPath("/root/a")
assert prim.GetAttribute("my_custom_attr").Get() == 10

# Duplicate things
new_prim_path = duplicate_prim(prim)

# Ensure the new prim is also as we want it, of Xform type with the custom attribute
# and that the opinions appear in a single prim spec (single layer)
new_prim = stage.GetPrimAtPath("/root/a1")
assert new_prim.IsValid(), "New prim must exist"
print(stage.ExportToString())
assert new_prim.GetAttribute("my_custom_attr").Get() == 10
assert new_prim.GetTypeName() == "Xform"
assert len(new_prim.GetPrimStack()) == 1, "Must be on a single layer"
assert new_prim.GetPrimStack()[0].layer == stage.GetEditTarget().GetLayer(), "New prim must be on the current edit target layer"

```

### Prototype 2: Duplicate prim with UsdUtils.StitchLayers

And here’s an example that does clone its children by just using a temporary `Sdf.Layer` per spec in the prim’s stack and then doing a full stitch layers:

```python
from pxr import Sdf, Usd, UsdUtils, UsdGeom

def unique_name(stage: Usd.Stage, prim_path: Sdf.Path) -> Sdf.Path:
    """Return Sdf.Path that is unique under the current composed stage.

    Note that this technically does not ensure that the Sdf.Path does not
    exist in any of the layers, e.g. it could be defined within a currently
    unselected variant or a muted layer.

    """
    src = prim_path.pathString.rstrip("123456789")
    i = 1
    while stage.GetPrimAtPath(prim_path):
        prim_path = Sdf.Path(f"{src}{i}")
        i += 1
    return prim_path

def duplicate_prim(prim: Usd.Prim) -> list[Sdf.Path]:
    """Duplicate prim flattened into current edit target"""
    path = prim.GetPath()
    stage = prim.GetStage()
    
    # Copy spec to a temp layer where we use stitch utiltiies
    # to merge the opinions of multiple layers of the original
    # prim's PrimStack so we have a 'merged opinion' on one
    # layer
    tmp_layers = []
    for spec in prim.GetPrimStack():
        tmp_layer = Sdf.Layer.CreateAnonymous()
        Sdf.CreatePrimInLayer(tmp_layer, path.GetParentPath())
        if not Sdf.CopySpec(spec.layer, path, tmp_layer, path):
            raise RuntimeError("No spec copied")
        
        tmp_layers.append(tmp_layer)
    
    # Add the additional opinions that make the original prim stack
    new_spec = tmp_layer.GetPrimAtPath(path)
    full_layer = Sdf.Layer.CreateAnonymous()
    for tmp_layer in tmp_layers:
        UsdUtils.StitchLayers(full_layer, tmp_layer) 
    
    # Now copy the flattened spec to the target "duplicate"
    target = stage.GetEditTarget()
    target_layer = target.GetLayer()
    new_path = unique_name(stage, path)
    new_path = target.MapToSpecPath(new_path)
    Sdf.CopySpec(full_layer, path, target_layer, new_path)
    
    return new_path

# Create a stage with an Xform prim
stage = Usd.Stage.CreateInMemory()
prim = stage.DefinePrim("/root/a", "Xform")
prim = stage.DefinePrim("/root/a/child_in_rootlayer", "Xform")

# Now add an opinion in another layer
layer = Sdf.Layer.CreateAnonymous()
prim_spec = Sdf.CreatePrimInLayer(layer, "/root/a")
attr_spec = Sdf.AttributeSpec(prim_spec, "my_custom_attr", Sdf.ValueTypeNames.Double)
attr_spec.default = 10
attr_spec.interpolation = UsdGeom.Tokens.constant
spec = Sdf.CreatePrimInLayer(layer, "/root/a/child_in_sublayer")
spec.specifier = Sdf.SpecifierDef
spec.typeName = "Xform"

# Sublayer the path ontot the stage
root_layer = stage.GetRootLayer()
root_layer.subLayerPaths.append(layer.identifier)

# Debugging: Make sure the attribute appears on the composed stage
prim = stage.GetPrimAtPath("/root/a")
assert prim.GetAttribute("my_custom_attr").Get() == 10
assert stage.GetPrimAtPath("/root/a/child_in_rootlayer")
assert stage.GetPrimAtPath("/root/a/child_in_sublayer")

# Duplicate things
new_prim_path = duplicate_prim(prim)

# Ensure the new prim is also as we want it, of Xform type with the custom attribute
# and that the opinions appear in a single prim spec (single layer)
new_prim = stage.GetPrimAtPath("/root/a1")
assert new_prim.IsValid(), "New prim must exist"
print(stage.ExportToString())
assert new_prim.GetAttribute("my_custom_attr").Get() == 10
assert new_prim.GetTypeName() == "Xform"
assert len(new_prim.GetPrimStack()) == 1, "Must be on a single layer"
assert new_prim.GetPrimStack()[0].layer == stage.GetEditTarget().GetLayer(), "New prim must be on the current edit target layer"
assert stage.GetPrimAtPath("/root/a1/child_in_rootlayer")
assert stage.GetPrimAtPath("/root/a1/child_in_sublayer")

```

---

<div class="post-metadata">

**Author:** ![zdarova](https://sea2.discourse-cdn.com/flex016/user_avatar/forum.aousd.org/zdarova/32/345_2.png) [@zdarova](https://forum.aousd.org/u/zdarova)\
**Post date:** [December 5, 2023, 12:27pm UTC](https://forum.aousd.org/t/how-to-duplicate-a-prim/1008/7 "2023-12-05T12:27:27Z")

</div>

HUGE thanks! Your first answer (with Sdf.CopySpec) was enough for my needs.

I hope, your detailed answer (with examples) will be useful not only for me but for all community, cause there is no beginner-friendly information about prim duplication.
