├── 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|[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/philsawicki/usd-core-on-google-colab/blob/main/USD-core-on-Google-Colab.ipynb)| 19 | |Fast export|[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/philsawicki/usd-core-on-google-colab/blob/main/notebooks/fast-export.ipynb)| 20 | |Get SDF Prim path|[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/philsawicki/usd-core-on-google-colab/blob/main/notebooks/get-sdf-prim-path.ipynb)| 21 | |SDF change block|[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](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 | } --------------------------------------------------------------------------------