├── LICENSE
├── README.md
├── notebooks
├── sdf-change-block.ipynb
├── fast-export.ipynb
└── get-sdf-prim-path.ipynb
└── USD-core-on-Google-Colab.ipynb
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Philippe Sawicki
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # USD Core on Google Colab
2 |
3 | ## About
4 | This project demonstrate the use of USD Python APIs via Google Colaboratory notebook.
5 |
6 | The goal is to make it easy to:
7 | * Try code snippets without building or installing Pixar's Universal Scene Description (USD) locally;
8 | * Share code snippets without copy/pasting files and scripts to a local machine, thus mitigating some security concerns;
9 | * Provide repro steps when submitting issues to [USD's GitHub repository](https://github.com/PixarAnimationStudios/USD);
10 | * etc.
11 |
12 | ## Sample Notebooks
13 |
14 | A few sample notebooks inspired from Colin Kennedy's excellent [USD Cookbook](https://github.com/ColinKennedy/USD-Cookbook):
15 |
16 | |Notebook|Google Colab link|
17 | |---|---|
18 | |Flattening a USD Stage|[](https://colab.research.google.com/github/philsawicki/usd-core-on-google-colab/blob/main/USD-core-on-Google-Colab.ipynb)|
19 | |Fast export|[](https://colab.research.google.com/github/philsawicki/usd-core-on-google-colab/blob/main/notebooks/fast-export.ipynb)|
20 | |Get SDF Prim path|[](https://colab.research.google.com/github/philsawicki/usd-core-on-google-colab/blob/main/notebooks/get-sdf-prim-path.ipynb)|
21 | |SDF change block|[](https://colab.research.google.com/github/philsawicki/usd-core-on-google-colab/blob/main/notebooks/sdf-change-block.ipynb)|
22 |
--------------------------------------------------------------------------------
/notebooks/sdf-change-block.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "nbformat": 4,
3 | "nbformat_minor": 0,
4 | "metadata": {
5 | "colab": {
6 | "name": "sdf-change-block.ipynb",
7 | "provenance": []
8 | },
9 | "kernelspec": {
10 | "name": "python3",
11 | "display_name": "Python 3"
12 | }
13 | },
14 | "cells": [
15 | {
16 | "cell_type": "markdown",
17 | "metadata": {
18 | "id": "hy40fqzodgo9"
19 | },
20 | "source": [
21 | "# SDF change block\n",
22 | "\n",
23 | "(**Note:** Script adapted from Colin Kennedy's excellent [USD Cookbook](https://github.com/ColinKennedy/USD-Cookbook/tree/master/features/sdf_change_block).)\n",
24 | "\n",
25 | "## Quick Reference\n",
26 | "\n",
27 | "USD's Change Processing sends notifications whenever changes are made. However, you can make significantly speed up your Sdf calls if you batch the changes into a single operation, which you can do using `SdfChangeBlock`.\n",
28 | "\n",
29 | "Because `SdfChangeBlock` is easy to use poorly, [check out the documentation](https://graphics.pixar.com/usd/docs/api/class_sdf_change_block.html) before using it in your own tools."
30 | ]
31 | },
32 | {
33 | "cell_type": "code",
34 | "metadata": {
35 | "id": "wZJqonzadQww"
36 | },
37 | "source": [
38 | "! pip install usd-core\n",
39 | "\n",
40 | "# See https://pypi.org/project/usd-core/#history for a list of supported USD\n",
41 | "# versions."
42 | ],
43 | "execution_count": null,
44 | "outputs": []
45 | },
46 | {
47 | "cell_type": "code",
48 | "metadata": {
49 | "id": "n174N2c0dqC6"
50 | },
51 | "source": [
52 | "\"\"\"Batch-creating PrimSpecs, using SdfChangeBlock.\"\"\"\n",
53 | "\n",
54 | "# IMPORT THIRD-PARTY LIBRARIES\n",
55 | "from pxr import Sdf, UsdGeom\n",
56 | "\n",
57 | "\n",
58 | "def main():\n",
59 | " \"\"\"Run the main execution of the current script.\"\"\"\n",
60 | " layer = Sdf.Layer.CreateAnonymous()\n",
61 | "\n",
62 | " paths = {\n",
63 | " Sdf.Path(\"/AndMore\"),\n",
64 | " Sdf.Path(\"/AnotherOne\"),\n",
65 | " Sdf.Path(\"/AnotherOne/AndAnother\"),\n",
66 | " Sdf.Path(\"/More\"),\n",
67 | " Sdf.Path(\"/OkayNoMore\"),\n",
68 | " Sdf.Path(\"/SomeSphere\"),\n",
69 | " Sdf.Path(\"/SomeSphere/InnerPrim\"),\n",
70 | " Sdf.Path(\"/SomeSphere/InnerPrim/LastOne\"),\n",
71 | " }\n",
72 | "\n",
73 | " prefixes = set(prefix for path in paths for prefix in path.GetPrefixes())\n",
74 | " with Sdf.ChangeBlock():\n",
75 | " for path in prefixes:\n",
76 | " prim_spec = Sdf.CreatePrimInLayer(layer, path)\n",
77 | " prim_spec.specifier = Sdf.SpecifierDef\n",
78 | " prim_spec.typeName = UsdGeom.Xform.__name__\n",
79 | "\n",
80 | " print(layer.ExportToString())\n",
81 | "\n",
82 | "\n",
83 | "if __name__ == \"__main__\":\n",
84 | " main()"
85 | ],
86 | "execution_count": null,
87 | "outputs": []
88 | }
89 | ]
90 | }
--------------------------------------------------------------------------------
/notebooks/fast-export.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "nbformat": 4,
3 | "nbformat_minor": 0,
4 | "metadata": {
5 | "colab": {
6 | "name": "fast-export.ipynb",
7 | "provenance": []
8 | },
9 | "kernelspec": {
10 | "name": "python3",
11 | "display_name": "Python 3"
12 | }
13 | },
14 | "cells": [
15 | {
16 | "cell_type": "markdown",
17 | "metadata": {
18 | "id": "oBiWoOvqX56J"
19 | },
20 | "source": [
21 | "# USD fast export\n",
22 | "\n",
23 | "(**Note:** Script adapted from Colin Kennedy's excellent [USD Cookbook](https://github.com/ColinKennedy/USD-Cookbook/tree/master/tricks/fast_export).)\n",
24 | "\n",
25 | "## Quick Reference\n",
26 | "\n",
27 | "The USD documentation states that defining Prims and reparenting prims are some of the sloweest operations that USD does.\n",
28 | "\n",
29 | "That said, USD's `Sdf` API is fast at authoring `PrimSpec`s so when running across a suggestion from the USD forums to split an export into two passes (one for the `PrimSpec`s and one for the properties/attributes), it turns out the `Sdf` version is over 100x faster than authoring the same `Prim`s in a USD stage!"
30 | ]
31 | },
32 | {
33 | "cell_type": "code",
34 | "metadata": {
35 | "id": "Yd9VSRkjXx7F"
36 | },
37 | "source": [
38 | "! pip install usd-core\n",
39 | "\n",
40 | "# See https://pypi.org/project/usd-core/#history for a list of supported USD\n",
41 | "# versions."
42 | ],
43 | "execution_count": null,
44 | "outputs": []
45 | },
46 | {
47 | "cell_type": "code",
48 | "metadata": {
49 | "id": "mzGxr5bDY3WC"
50 | },
51 | "source": [
52 | "# IMPORT STANDARD LIBRARIES\n",
53 | "import collections\n",
54 | "import time\n",
55 | "\n",
56 | "# IMPORT THIRD-PARTY LIBRARIES\n",
57 | "from pxr import Sdf, Usd\n",
58 | "\n",
59 | "ITERATIONS = 1000\n",
60 | "PATHS = frozenset((\n",
61 | " \"/BasePrim\",\n",
62 | " \"/BasePrim/InnerPrim\",\n",
63 | " \"/BasePrim/InnerPrim/SiblingPrim\",\n",
64 | " \"/SomePrim\",\n",
65 | " \"/SomePrim/AnotherInnerPrim\",\n",
66 | " \"/SomePrim/ChildPrim\",\n",
67 | " \"/SomePrim/SiblingPrim\"\n",
68 | "))\n",
69 | "\n",
70 | "\n",
71 | "# Reference: https://medium.com/pythonhive/python-decorator-to-measure-the-execution-time-of-methods-fa04cb6bb36d\n",
72 | "def _timeit(method):\n",
73 | " def timed(*args, **kw):\n",
74 | " ts = time.time()\n",
75 | " result = method(*args, **kw)\n",
76 | " te = time.time()\n",
77 | " if 'log_time' in kw:\n",
78 | " name = kw.get('log_name', method.__name__.upper())\n",
79 | " kw['log_time'][name] = int((te - ts) * 1000)\n",
80 | " else:\n",
81 | " print('%r %2.2f ms' % (method.__name__, (te - ts) * 1000))\n",
82 | " return result\n",
83 | " return timed\n",
84 | "\n",
85 | "\n",
86 | "@_timeit\n",
87 | "def _prepare_prim_specs_with_sdf(layer, paths):\n",
88 | " \"\"\"Create PrimSpecs using a Sdf Layer.\"\"\"\n",
89 | " for path in paths:\n",
90 | " prim_spec = Sdf.CreatePrimInLayer(layer, path)\n",
91 | " prim_spec.specifier = Sdf.SpecifierDef\n",
92 | "\n",
93 | " parent = layer.GetPrimAtPath(\"SomePrim/AnotherInnerPrim\")\n",
94 | " for index in range(ITERATIONS):\n",
95 | " Sdf.PrimSpec(parent, \"IndexedPrim{}\".format(index), Sdf.SpecifierDef)\n",
96 | "\n",
97 | "\n",
98 | "@_timeit\n",
99 | "def _prepare_prims_with_stage(stage, paths):\n",
100 | " \"\"\"Create Prims using a USD Stage.\"\"\"\n",
101 | " for path in paths:\n",
102 | " stage.DefinePrim(path)\n",
103 | "\n",
104 | " indexed_template = \"/SomePrim/AnotherInnerPrim/IndexedPrim{}\"\n",
105 | " for index in range(ITERATIONS):\n",
106 | " stage.DefinePrim(indexed_template.format(index))\n",
107 | "\n",
108 | "\n",
109 | "def create_using_sdf():\n",
110 | " \"\"\"Run the main execution of the current script.\"\"\"\n",
111 | " layer = Sdf.Layer.CreateAnonymous()\n",
112 | "\n",
113 | " # TODO : Adding / Removing this ChangeBlock doesn't change the time\n",
114 | " # much. Is a change block only useful when authoring opinions?\n",
115 | " #\n",
116 | " with Sdf.ChangeBlock():\n",
117 | " _prepare_prim_specs_with_sdf(layer, PATHS)\n",
118 | "\n",
119 | " return layer.ExportToString()\n",
120 | "\n",
121 | "\n",
122 | "def create_using_stage():\n",
123 | " \"\"\"str: Create Prims using a USD stage.\"\"\"\n",
124 | " stage = Usd.Stage.CreateInMemory()\n",
125 | " _prepare_prims_with_stage(stage, PATHS)\n",
126 | "\n",
127 | " return stage.GetRootLayer().ExportToString()\n",
128 | "\n",
129 | "\n",
130 | "def main():\n",
131 | " stage_export = create_using_stage()\n",
132 | " layer_export = create_using_sdf()\n",
133 | "\n",
134 | " # The first line of a USD export is a metadata line so we remove it\n",
135 | " # here just so we can compare if the output really is the same.\n",
136 | " #\n",
137 | " stage_export = stage_export.splitlines()[1:]\n",
138 | " layer_export = layer_export.splitlines()[1:]\n",
139 | "\n",
140 | " print('These exports should be exactly the same', stage_export == layer_export)\n",
141 | "\n",
142 | "\n",
143 | "if __name__ == \"__main__\":\n",
144 | " main()"
145 | ],
146 | "execution_count": null,
147 | "outputs": []
148 | }
149 | ]
150 | }
--------------------------------------------------------------------------------
/USD-core-on-Google-Colab.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "nbformat": 4,
3 | "nbformat_minor": 0,
4 | "metadata": {
5 | "colab": {
6 | "name": "USD-core-on-Google-Colab.ipynb",
7 | "provenance": [],
8 | "collapsed_sections": []
9 | },
10 | "kernelspec": {
11 | "name": "python3",
12 | "display_name": "Python 3"
13 | }
14 | },
15 | "cells": [
16 | {
17 | "cell_type": "markdown",
18 | "metadata": {
19 | "id": "d7SGPe5YpUm3"
20 | },
21 | "source": [
22 | "# Using USD Python APIs on Google Colab\n",
23 | "\n",
24 | "This Google Colaboratory notebook illustrates how to use the core USD Python API from a web browser. \n",
25 | "\n",
26 | "The goal is to make it easy to:\n",
27 | " * Try code snippets without building or installing USD locally;\n",
28 | " * Share code snippets without copy/pasting files and scripts to a local machine, thus mitigating some security concerns;\n",
29 | " * Provide repro steps when submitting issues to [USD's GitHub repository](https://github.com/PixarAnimationStudios/USD);\n",
30 | " * etc. \n",
31 | "\n",
32 | "**To run this sample:** click _Runtime_ > _Run all_ from the top menu, or use the ⌘/CTRL+F9 keyboard shortcut."
33 | ]
34 | },
35 | {
36 | "cell_type": "markdown",
37 | "metadata": {
38 | "id": "-9aGK7fXmghS"
39 | },
40 | "source": [
41 | "## Install the `usd-core` Python package\n",
42 | "\n",
43 | "Install the [`usd-core`](https://pypi.org/project/usd-core/) Python package providing the core USD libraries. Note that it does not provide any of the optional plugins or imaging features from the complete USD distribution."
44 | ]
45 | },
46 | {
47 | "cell_type": "code",
48 | "metadata": {
49 | "id": "epRX-Z0OmCkp"
50 | },
51 | "source": [
52 | "! pip install usd-core\n",
53 | "\n",
54 | "# See https://pypi.org/project/usd-core/#history for a list of supported USD\n",
55 | "# versions."
56 | ],
57 | "execution_count": null,
58 | "outputs": []
59 | },
60 | {
61 | "cell_type": "markdown",
62 | "metadata": {
63 | "id": "Tgskn4nFmcJ1"
64 | },
65 | "source": [
66 | "## Create a sample USD file\n",
67 | "\n",
68 | "This example will create a `Sphere` with shading variants, which will later be loaded in memory and flattened via a script to demonstrate how to use the Python API in Google Colab.\n",
69 | "\n",
70 | "For additional context and background information about the source of this USDA file, see Pixar's [_Authoring Variants_](https://graphics.pixar.com/usd/docs/Authoring-Variants.html) tutorial."
71 | ]
72 | },
73 | {
74 | "cell_type": "code",
75 | "metadata": {
76 | "id": "s9wbSCammXys"
77 | },
78 | "source": [
79 | "%%file sphere-variant.usda\n",
80 | "#usda 1.0\n",
81 | "(\n",
82 | " defaultPrim = \"hello\"\n",
83 | ")\n",
84 | "\n",
85 | "def Xform \"hello\" (\n",
86 | " variants = {\n",
87 | " string shadingVariant = \"green\"\n",
88 | " }\n",
89 | " prepend variantSets = \"shadingVariant\"\n",
90 | ") {\n",
91 | " custom double3 xformOp:translate = (4, 5, 6)\n",
92 | " uniform token[] xformOpOrder = [\"xformOp:translate\"]\n",
93 | "\n",
94 | " def Sphere \"world\" {\n",
95 | " float3[] extent = [(-2, -2, -2), (2, 2, 2)]\n",
96 | " color3f[] primvars:displayColor\n",
97 | " double radius = 2\n",
98 | " }\n",
99 | "\n",
100 | " variantSet \"shadingVariant\" = {\n",
101 | " \"red\" {\n",
102 | " over \"world\" {\n",
103 | " color3f[] primvars:displayColor = [(1, 0, 0)]\n",
104 | " }\n",
105 | " }\n",
106 | " \"green\" {\n",
107 | " over \"world\" {\n",
108 | " color3f[] primvars:displayColor = [(0, 1, 0)]\n",
109 | " }\n",
110 | " }\n",
111 | " \"blue\" {\n",
112 | " over \"world\" {\n",
113 | " color3f[] primvars:displayColor = [(0, 0, 1)]\n",
114 | " }\n",
115 | " }\n",
116 | " }\n",
117 | "}"
118 | ],
119 | "execution_count": null,
120 | "outputs": []
121 | },
122 | {
123 | "cell_type": "markdown",
124 | "metadata": {
125 | "id": "5DWbOKfmoPgw"
126 | },
127 | "source": [
128 | "## Flatten the Stage using the USD Python API"
129 | ]
130 | },
131 | {
132 | "cell_type": "code",
133 | "metadata": {
134 | "id": "rfhJSz2Mm1qr"
135 | },
136 | "source": [
137 | "from pxr import Usd\n",
138 | "\n",
139 | "\n",
140 | "def main():\n",
141 | " \"\"\"\n",
142 | " Open the \"sphere-variant.usda\" file, flatten the stage and print the result\n",
143 | " for inspection, to observe the \"green\" shading variant has been applied.\n",
144 | " \"\"\"\n",
145 | " stage = Usd.Stage.Open(\"sphere-variant.usda\")\n",
146 | " falttenedStage = stage.Flatten().ExportToString()\n",
147 | " print(falttenedStage)\n",
148 | "\n",
149 | "if __name__ == '__main__':\n",
150 | " main()"
151 | ],
152 | "execution_count": null,
153 | "outputs": []
154 | },
155 | {
156 | "cell_type": "markdown",
157 | "metadata": {
158 | "id": "Mxi_-wW3vr9K"
159 | },
160 | "source": [
161 | "## Now try your own snippet!"
162 | ]
163 | },
164 | {
165 | "cell_type": "code",
166 | "metadata": {
167 | "id": "77iSnDJNvy1q"
168 | },
169 | "source": [
170 | "# Double-click this cell to enter your Python script, then run it by either:\n",
171 | "# * Clicking the \"play\" icon next to the cell.\n",
172 | "# * Using the CMD/CTRL+ENTER keyboard shortcut.\n",
173 | "\n"
174 | ],
175 | "execution_count": null,
176 | "outputs": []
177 | }
178 | ]
179 | }
--------------------------------------------------------------------------------
/notebooks/get-sdf-prim-path.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "nbformat": 4,
3 | "nbformat_minor": 0,
4 | "metadata": {
5 | "colab": {
6 | "name": "get-sdf-prim-path.ipynb",
7 | "provenance": []
8 | },
9 | "kernelspec": {
10 | "name": "python3",
11 | "display_name": "Python 3"
12 | }
13 | },
14 | "cells": [
15 | {
16 | "cell_type": "markdown",
17 | "metadata": {
18 | "id": "4wHzfbATa2-y"
19 | },
20 | "source": [
21 | "# Get SDF Prim path\n",
22 | "\n",
23 | "(**Note:** Script adapted from Colin Kennedy's excellent [USD Cookbook](https://github.com/ColinKennedy/USD-Cookbook/tree/master/tricks/get_sdf_prim_path).)\n",
24 | "\n",
25 | "## Quick Reference\n",
26 | "\n",
27 | "Sometimes, you just need to select a Prim. If you have no variant sets, you can normally just do `stage.GetPrimAtPath(\"/foo/bar\")` and you're done. But what if \"bar\" is defined behind a variant set and that variant set isn't selected in the current stage? Then `GetPrimAtPath` returns an invalid Prim.\n",
28 | "\n",
29 | "The basic steps goes like this:\n",
30 | " * Have a description of the entire path (including variants + their selections)\n",
31 | " * Iterate overy every variant set and forcibly set the selections, one by one\n",
32 | " * Then you can get your path"
33 | ]
34 | },
35 | {
36 | "cell_type": "code",
37 | "metadata": {
38 | "id": "nL5H0DN8ayNc"
39 | },
40 | "source": [
41 | "! pip install usd-core\n",
42 | "\n",
43 | "# See https://pypi.org/project/usd-core/#history for a list of supported USD\n",
44 | "# versions."
45 | ],
46 | "execution_count": null,
47 | "outputs": []
48 | },
49 | {
50 | "cell_type": "code",
51 | "metadata": {
52 | "id": "I7hqRT_Wbfio"
53 | },
54 | "source": [
55 | "%%file sample.usda\n",
56 | "#usda 1.0\n",
57 | "def Scope \"root\" (\n",
58 | " variantSets = [\"foo\"]\n",
59 | ") {\n",
60 | " variantSet \"foo\" = {\n",
61 | " \"base\" {\n",
62 | " def Scope \"prim1\" {\n",
63 | " def Sphere \"a_sphere\" {\n",
64 | " double radius = 3\n",
65 | " }\n",
66 | " }\n",
67 | " }\n",
68 | " \"another\" {\n",
69 | " def Scope \"prim2\" (\n",
70 | " variantSets = [\"bar\"]\n",
71 | " ) {\n",
72 | " variantSet \"bar\" = {\n",
73 | " \"one\" {\n",
74 | " def Sphere \"sphere\" {\n",
75 | " double radius = 2\n",
76 | " }\n",
77 | " }\n",
78 | " }\n",
79 | " }\n",
80 | " }\n",
81 | " }\n",
82 | "}"
83 | ],
84 | "execution_count": null,
85 | "outputs": []
86 | },
87 | {
88 | "cell_type": "code",
89 | "metadata": {
90 | "id": "JZ6_GwjrbOHx"
91 | },
92 | "source": [
93 | "\"\"\"A module used to get any Prim in a USD stage.\"\"\"\n",
94 | "\n",
95 | "from pxr import Sdf, Usd, UsdGeom\n",
96 | "\n",
97 | "\n",
98 | "def _iter_all_parents(path):\n",
99 | " \"\"\"Get every Sdf namespace on-and-above `path`.\n",
100 | " This function is inclusive - which means it returns `path` as part of its output.\n",
101 | " Args:\n",
102 | " path (:class:`pxr.Sdf.Path`):\n",
103 | " A Sdf namespace to some Prim. e.g. \"/foo/bar\", \"/some{variant_set=selection}here\", etc.\n",
104 | " Yields:\n",
105 | " :class:`pxr.Sdf.Path`: The given `path` and any found parents.\n",
106 | " \"\"\"\n",
107 | " parent = path\n",
108 | "\n",
109 | " while not parent.IsRootPrimPath():\n",
110 | " yield parent\n",
111 | " parent = parent.GetParentPath()\n",
112 | "\n",
113 | " # Yield the root Prim path, too\n",
114 | " yield parent\n",
115 | "\n",
116 | "\n",
117 | "def _gather_variant_selections(path):\n",
118 | " \"\"\"Parse `path` into all of its variant set / selection details.\n",
119 | " Args:\n",
120 | " path (:class:`pxr.Sdf.Path`):\n",
121 | " A Sdf namespace to some Prim. e.g. \"/foo/bar\", \"/some{variant_set=selection}here\", etc.\n",
122 | " Returns:\n",
123 | " list[tuple[str, str, str]]:\n",
124 | " This output describes the variants `path` contains. Starting\n",
125 | " from `path`'s top-most parent down to the bottom, it\n",
126 | " returns a variant-less path + its variant data. e.g.\n",
127 | " \"/some{variant_set=selection}here\" returns. [(\"/some\",\n",
128 | " \"variant_set\", \"selection\")].\n",
129 | " \"\"\"\n",
130 | " output = []\n",
131 | "\n",
132 | " for path_ in reversed(list(_iter_all_parents(path))):\n",
133 | " variant_set, selection = path_.GetVariantSelection()\n",
134 | "\n",
135 | " if not variant_set or not selection:\n",
136 | " continue\n",
137 | "\n",
138 | " output.append((path_.StripAllVariantSelections(), variant_set, selection))\n",
139 | "\n",
140 | " return output\n",
141 | "\n",
142 | "\n",
143 | "def get_prim_at_path(stage, path):\n",
144 | " \"\"\"Get the Prim at `path`, using some `stage`.\n",
145 | " Warning:\n",
146 | " This function will modify the current state of `stage` to\n",
147 | " forcibly get the Prim at `path`. If you don't want this, you're\n",
148 | " better of using :func:`pxr.Usd.Stage.GetPrimAtPath`.\n",
149 | " Args:\n",
150 | " stage (:class:`pxr.Usd.Stage`):\n",
151 | " Some layer that contains Prims. Presumably, it also contains a Prim at `path`.\n",
152 | " path (:class:`pxr.Sdf.Path`):\n",
153 | " Some absolute path to a Prim which is assumed to live in\n",
154 | " `stage`. If the Prim lies inside of variant sets, make sure to include\n",
155 | " those details. e.g. `Sdf.Path(\"/foo{variant_set=selection}prim1\")`.\n",
156 | " Raises:\n",
157 | " ValueError: If `path` is a valid Sdf Path but cannot be used by this function.\n",
158 | " Returns:\n",
159 | " :class:`pxr.Usd.Prim`: The found Prim at `path`.\n",
160 | " \"\"\"\n",
161 | " if not path.ContainsPrimVariantSelection():\n",
162 | " return stage.GetPrimAtPath(path)\n",
163 | "\n",
164 | " root = path.GetPrimOrPrimVariantSelectionPath()\n",
165 | "\n",
166 | " if str(root).endswith(\"}\"):\n",
167 | " raise ValueError(\n",
168 | " 'Path \"{root}\" is not allowed. You cannot select a variant set directly.'.format(\n",
169 | " root=root\n",
170 | " )\n",
171 | " )\n",
172 | "\n",
173 | " for selector, variant_set, selection in _gather_variant_selections(root):\n",
174 | " prim = stage.GetPrimAtPath(selector)\n",
175 | " selector = prim.GetVariantSets().GetVariantSet(variant_set)\n",
176 | " selector.SetVariantSelection(selection)\n",
177 | "\n",
178 | " # Now that `stage` is in the correct state and every variant set\n",
179 | " # has been applied, we can finally select the Prim that `path` describes.\n",
180 | " #\n",
181 | " composed_path = path.StripAllVariantSelections()\n",
182 | "\n",
183 | " return stage.GetPrimAtPath(composed_path.GetPrimPath())\n",
184 | "\n",
185 | "\n",
186 | "def main():\n",
187 | " \"\"\"Run the main execution of the current script.\"\"\"\n",
188 | " stage = Usd.Stage.Open(\"sample.usda\")\n",
189 | "\n",
190 | " variant_sphere = UsdGeom.Sphere(get_prim_at_path(stage, Sdf.Path(\"/root{foo=base}prim1/a_sphere\")))\n",
191 | " print('This value should be 3.0: \"{}\"'.format(variant_sphere.GetRadiusAttr().Get()))\n",
192 | "\n",
193 | " nested_variant_sphere = UsdGeom.Sphere(get_prim_at_path(stage, Sdf.Path(\"/root{foo=another}prim2{bar=one}sphere\")))\n",
194 | " print('This value should be 2.0: \"{}\"'.format(nested_variant_sphere.GetRadiusAttr().Get()))\n",
195 | "\n",
196 | "\n",
197 | "if __name__ == \"__main__\":\n",
198 | " main()"
199 | ],
200 | "execution_count": null,
201 | "outputs": []
202 | }
203 | ]
204 | }
--------------------------------------------------------------------------------