diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 09746ee2ef..b43d3fadf0 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -3,6 +3,11 @@ ### Fixes Issue(s) - + +- [ ] I have verified that all unit tests pass with the proposed changes Complexity` menu item or use the hotkeys :kbd:`Ctrl-+` and :kbd:`Ctrl--` to increase or decrease the refinement. - .. image:: http://graphics.pixar.com/usd/docs/attachments/368705904/568921522.png + .. image:: http://graphics.pixar.com/usd/docs/attachments/368705904/HelloWorld_Sphere_2.png #. You can also bring up an embedded Python interpreter by pressing :kbd:`i` or using the :menuselection:`Window --> Interpreter` menu item. This interpreter diff --git a/docs/tut_inspect_and_author_props.rst b/docs/tut_inspect_and_author_props.rst index a8d858ccfb..5f358b55be 100644 --- a/docs/tut_inspect_and_author_props.rst +++ b/docs/tut_inspect_and_author_props.rst @@ -135,7 +135,7 @@ Tutorial #. Here is the result in usdview. - .. image:: http://graphics.pixar.com/usd/docs/attachments/368706056/565776849.png + .. image:: http://graphics.pixar.com/usd/docs/attachments/368706056/FinalResult_usdview.png The camera automatically frames the geometry, but we can see that the sphere is larger than in the last tutorial by inspecting its attributes in the diff --git a/docs/tut_referencing_layers.rst b/docs/tut_referencing_layers.rst index cd2a36c590..5d6efb0803 100644 --- a/docs/tut_referencing_layers.rst +++ b/docs/tut_referencing_layers.rst @@ -123,7 +123,7 @@ will use as our starting point and all the code for this exercise is in the Running usdview on the exported :filename:`RefExample.usda` shows the composed result. - .. image:: http://graphics.pixar.com/usd/docs/attachments/368706187/585276477.png + .. image:: http://graphics.pixar.com/usd/docs/attachments/368706187/RefExample.png If it were unselected in the namespace browser, usdview would show :sdfpath:`/refSphere` in orange to indicate that it is a referencing point on @@ -201,7 +201,7 @@ will use as our starting point and all the code for this exercise is in the { } - .. image:: http://graphics.pixar.com/usd/docs/attachments/368706187/565776852.png + .. image:: http://graphics.pixar.com/usd/docs/attachments/368706187/RefExample_2.png We can see that our over has been applied to move the first sphere to the origin, while the second sphere is still translated by :code:`(4, 5, 6)`. @@ -243,7 +243,7 @@ will use as our starting point and all the code for this exercise is in the } } - .. image:: http://graphics.pixar.com/usd/docs/attachments/368706187/565776853.png + .. image:: http://graphics.pixar.com/usd/docs/attachments/368706187/RefExample_3.png #. We can also flatten the composed results. All of the scene description listings above were of the stage's root layer, where we performed our diff --git a/docs/tut_setup_version_badge.rst b/docs/tut_setup_version_badge.rst index e261e5e099..fcf09aee51 100644 --- a/docs/tut_setup_version_badge.rst +++ b/docs/tut_setup_version_badge.rst @@ -2,4 +2,4 @@ :fa:`cogs` :ref:`Configure your Environment ` - :fa:`check` Tested with `USD 22.08 `_ + :fa:`check` Tested with `USD 22.11 `_ diff --git a/docs/tut_simple_shading.rst b/docs/tut_simple_shading.rst index 4031584295..60dfe2ce8d 100644 --- a/docs/tut_simple_shading.rst +++ b/docs/tut_simple_shading.rst @@ -72,7 +72,7 @@ which provide analytically computed normals to a renderer. billboard.CreateFaceVertexCountsAttr([4]) billboard.CreateFaceVertexIndicesAttr([0,1,2,3]) billboard.CreateExtentAttr([(-430, -145, 0), (430, 145, 0)]) - texCoords = billboard.CreatePrimvar("st", + texCoords = UsdGeom.PrimvarsAPI(billboard).CreatePrimvar("st", Sdf.ValueTypeNames.TexCoord2fArray, UsdGeom.Tokens.varying) texCoords.Set([(0, 0), (1, 0), (1,1), (0, 1)]) @@ -90,7 +90,7 @@ multiple times). In another command shell, try: We should see something like: -.. image:: http://graphics.pixar.com/usd/docs/attachments/580914176/580914267.png +.. image:: http://graphics.pixar.com/usd/docs/attachments/580914176/simpleShading.png Make a Material =============== @@ -175,15 +175,16 @@ shading prims. stReader.CreateInput('varname',Sdf.ValueTypeNames.Token).ConnectToSource(stInput) -And lastly, bind the Mesh to our Material and save the results! +And lastly, apply MaterialBindingAPI on the billboard prim, bind the Mesh to our Material and save the results! .. code-block:: python + billboard.GetPrim().ApplyAPI(UsdShade.MaterialBindingAPI) UsdShade.MaterialBindingAPI(billboard).Bind(material) stage.Save() In usdview, we should now see something like: -.. image:: http://graphics.pixar.com/usd/docs/attachments/580914176/585273640.png +.. image:: http://graphics.pixar.com/usd/docs/attachments/580914176/simpleShading_2.png diff --git a/docs/tut_traversing_stage.rst b/docs/tut_traversing_stage.rst index 24bf32c77d..63ee8ef2ac 100644 --- a/docs/tut_traversing_stage.rst +++ b/docs/tut_traversing_stage.rst @@ -69,7 +69,7 @@ folder to a working directory and make its contents writable. :menuselection:`Edit --> Deactivate` from the menu. The result should look like this. - .. image:: http://graphics.pixar.com/usd/docs/attachments/369066623/565776856.png + .. image:: http://graphics.pixar.com/usd/docs/attachments/369066623/RefExample_Trav_Stage.png You can inspect the contents of the session layer in the interpreter to see the authored deactivation opinion. diff --git a/docs/tut_usd_tutorials.rst b/docs/tut_usd_tutorials.rst index ab9b63d9c2..fef9ea2e3a 100644 --- a/docs/tut_usd_tutorials.rst +++ b/docs/tut_usd_tutorials.rst @@ -17,7 +17,9 @@ Environment Setup These tutorials use USD's built-in Python bindings almost exclusively, and some programs in the :doc:`toolset` rely on one another. So please set the following -environment variables to successfully complete the tutorials. +environment variables to successfully complete the tutorials. Make sure to use +the Python interpreter corresponding to the version used to build USD. These +tutorials assume the interpreter is named "python". +---------------------+------------+----------------------------------------+ |Variable |Meaning |Value | diff --git a/docs/wp.rst b/docs/wp.rst index 4b3794601b..0887f68e0f 100644 --- a/docs/wp.rst +++ b/docs/wp.rst @@ -13,6 +13,7 @@ Proposals wp_connectable_nodes wp_render_settings wp_rigid_body_physics + wp_stage_variables wp_usdaudio wp_usdshade diff --git a/docs/wp_stage_variables.rst b/docs/wp_stage_variables.rst new file mode 100644 index 0000000000..ff6a01ca4a --- /dev/null +++ b/docs/wp_stage_variables.rst @@ -0,0 +1,643 @@ +========================== +Stage Variable Expressions +========================== + +.. include:: rolesAndUtils.rst +.. include:: + +Copyright |copy| 2022, Pixar Animation Studios, *version 1.0* + +.. contents:: :local: + +Introduction +============ + +We propose adding the ability to specify expressions in scene description that +will be evaluated by USD at runtime. These expressions will be allowed to refer +to "stage variables," which are entries in a dictionary-valued layer metadata +field that (with some limitations) are composed across composition arcs. + +Support for these expressions will initially be limited to two specific areas: + +- Asset paths (including sublayer, reference, and payload asset paths as well as + asset path-valued attributes and metadata) +- Variant selections + +.. code-block:: usda + :caption: Mock-up with Stage Variables and Expressions + + #usda 1.0 + ( + stageVariables = { + string PROD = "s101" + string SHOT = "01" + bool IS_SPECIAL_SHOT = "`in(${SHOT}, ['01', '03', '05'])`" + string CROWDS_SHADING_VARIANT = "baked" + } + + # Examples of expressions in sublayer asset paths. + subLayers = [ + @`if(${IS_SPECIAL_SHOT}, "special_shot_overrides.usd")`@, + @`${PROD}_${SHOT}_fx.usd`@ + ] + ) + + # Examples of expressions in references and asset-valued attributes. + def "Sox" ( + references = @`${PROD}/Sox/usd/Sox.usd`@ + ) + { + asset skinTexture = @`Sox_${SHOT}_Texture.png`@ + } + + # Example of conditional variant selection + def "Buzz" ( + references = @Buzz/usd/Buzz.usd@ + variants = { + string modelVariant = "`if(eq(${SHOT}, "01"), "01_variant", "regular")`" + } + ) + { + # ... + } + + # Example of unified variant selection + def "CrowdCharA" ( + references = @CrowdCharA/usd/CrowdCharA@ + variants = { + string shadingVariant = "`${CROWDS_SHADING_VARIANT}`" + } + ) + { + # ... + } + + def "CrowdCharB" ( + references = @CrowdCharB/usd/CrowdCharB@ + variants = { + string shadingVariant = "`${CROWDS_SHADING_VARIANT}`" + } + ) + { + # ... + } + +Expressions +=========== + +Expressions are written in a custom domain-specific language. The syntax and +initial set of operations is TBD. In particular, expressions should be +functional and strongly-typed. The examples in this document are strawmen for +discussion. + +Expressions are represented as strings that are surrounded with backticks +(`````). This allows code to recognize expressions with a simple test, which helps +minimize performance cost when this feature is not being used and allows better +error messages. If we didn't explicitly identify expressions somehow, the only +way we'd know it was an expression would be to try to evaluate it (or at least +parse it). + +Since expressions are just decorated strings clients could just set them using +existing API for authoring asset paths and variant selections. For example: + +.. code-block:: python + + # Asset Paths: + assetPathWithExpr = Sdf.AssetPath('`if(eq(${SHOT}, "01"), "shot_01.usd", "shot_other.usd")`') + + # Variant Selections: + primSpec.variantSelections['modelVariant'] = '`if(eq(${SHOT}, "01"), "shot_01_variant", "shot_other_variant")`' + +Stage Variables +=============== + +Stage variables are the only scene description that expressions are allowed to +refer to. These variables may be strings, bools, or ints and are stored in a +dictionary-valued layer metadata field named ``stageVariables``. Stage variables +may themselves be expressions, so that common logic can be factored into a +single location for convenience and brevity. + +As the name implies, stage variables are stage metadata, meaning they must be +authored on either the root or session layer of a stage or in the root layer of +a referenced layer stack. See below for more discussion of the composition +behaviors. + +.. code-block:: usda + + #usda 1.0 + ( + stageVariables = { + string SHOT = "01" + string PROD = "r345" + bool IS_SHOT_01 = '`eq(${SHOT}, "01")`' + } + ) + + # ... + +Stage Variables DO NOT Compose Across Sublayers +----------------------------------------------- + +Like all other stage metadata, stage variables do not compose across sublayers +in a layer stack except for the session and root layers of a stage. + +This restriction is primarily needed because sublayers may themselves be +specified using expressions that rely on stage variables. If we allowed stage +variables to compose across sublayers, we'd introduce circular dependencies that +would be, at best, difficult to handle consistently. For example, + +.. code-block:: usda + + #usda 1.0 + ( + subLayers = [ + # sub_2.usd specifies stageVariables[MYFLAG] = 2 + @`if(eq(${MYFLAG}, 1), "sub_2.usd", "sub_1.usd")`@, + + # Specifies stageVariables[MYFLAG] = 1 + @prod.usd@ + ] + ) + + # ... + +Let's assume stage variables did compose across sublayers. This layer stack +could be evaluated two different ways: + +- If the expression is evaluated first, it will not find a MYFLAG stage var and + will load sub_1.usd . Then, prod.usd will be loaded, which introduces MYFLAG=1 + . If a user were to ask the stage for the composed stage vars, it would return + MYFLAG=1 , but that would be inconsistent with the fact that sub_1.usd had + been loaded. + +- If the weakest layer is evaluated first, the stage var MYFLAG=1 will be set, + which will cause the expression to evaluate to and load sub_2.usd , which sets + MYFLAG=2 . If a user were to ask for the composed stage vars, it would return + MYFLAG=2 (because it was introduced in a stronger sublayer), but again this + would be inconsistent with the sublayers that had actually been loaded. + +This example also shows that allowing composition across sublayers would make it +difficult (impossible?) to enable multithreaded loading of sublayers in layer +stacks, again because of intra-sublayer dependencies. + +Stage Variables DO Compose Across References / Payloads +------------------------------------------------------- + +If a referenced layer stack contains expressions that refer to stage variables, +the stage variables from the chain of referencing layer stacks will be composed +together and used to evaluate those expressions. However, note that the sublayer +restriction from above still applies, so only stage variables authored on the +root layer of the layer stacks will be considered. + +This ensures that assets that internally use expressions referring to stage +variables can be referenced by another asset and still work as expected. This +also gives the referencing asset the ability to override the stage variables +used by the referenced asset. For example: + +.. code-block:: usda + :caption: ModelGroup.usd + + #usda 1.0 + ( + stageVariables = { + string MODEL_VARIANT = "var_1" + } + ) + + def "ModelGroup" + { + def "Model_1" ( + variantSelections = { + string modelVariant = "`${MODEL_VARIANT}`" + } + ) { } + + def "Model_2" ( + variantSelections = { + string modelVariant = "`${MODEL_VARIANT}`" + } + ) { } + } + +.. code-block:: usda + :caption: Stage1.usd + + #usda 1.0 + + # The expected behavior would be to bring in ModelGroup + # with variant selection "modelVariant=var_1" for all + # models in the group. + def "ModelGroup" ( + references = @./ModelGroup.usd@ + ) + { + } + +.. code-block:: usda + :caption: Stage2.usd + + #usda 1.0 + ( + stageVariables = { + string MODEL_VARIANT = "var_2" + } + ) + + # Now all models in this model group come in with + # variantSelection "modelVariant=var_2" + def "ModelGroup" ( + references = @./ModelGroup.usd@ + ) + { + } + +Areas of Consideration +====================== + +Composition +----------- + +During composition, `Pcp `_ will inspect layer asset +paths in composition arcs (e.g, sublayers, references, payloads) to determine if +they contain an expression and, if so, will evaluate them using the stage +variables composed to that point in the composition graph. + +If the expression is successfully evaluated, Pcp will treat the resulting string +like it would any other authored asset path: it will try to find or open the +layer at that path to continue the composition process and issue a composition +error if it fails to do so. + +If the expression cannot be evaluated, Pcp will issue a composition error that +will include information about the expression. The expression may fail to +evaluate due to a syntax error, or because it references a stage variable that +has no value. This error is distinct from the "invalid asset path" error +mentioned above that is issued when a layer at a given asset path cannot be +opened. It's important to treat these separately to provide the user with good +diagnostics. However, if an expression evaluates to an empty string, it will be +treated as though there was no value and will *not* result in an "invalid asset +path" error. + +The behavior is the same for variant selections containing expressions. If the +expression is successfully evaluated Pcp will treat the resulting string like it +would an authored variant selection. If the expression fails to evaluate (for +the same reasons above), Pcp will issue a composition error including +information about the expression. + +Asset Path Attributes and Metadata +---------------------------------- + +Currently, calling :usdcpp:`UsdAttribute::Get` on asset path-valued attributes +returns an :usdcpp:`SdfAssetPath` containing two fields: + +- The "unresolved" asset path, which today is just the strongest authored value + for the attribute. + +- The "resolved" asset path, which is the result of resolving the above via + :usdcpp:`ArResolver`. If :usdcpp:`UsdAttribute` detects that the unresolved + asset path contains an expression, it will evaluate them using the stage + variables composed to the point in the composition graph where the asset path + was authored. + +If the expression is successfully evaluated into an asset path, +:usdcpp:`UsdAttribute` will return an :usdcpp:`SdfAssetPath` with the unresolved +path set to the evaluated asset path and the resolved path set to the result of +resolving that asset path. + +If the evaluation fails due to a syntax error, the unresolved asset path will be +set to the authored expression and no attempt to resolve that path will +occur. However, if the evaluation produces an asset path that just has a missing +variable substitution, the unresolved path will be set to that "partial" result +instead and :usdcpp:`UsdAttribute` will attempt to resolve that path. For example: + +.. code-block:: usda + + # Usd.Attribute.Get() returns Sdf.AssetPath('`bogus_expression("foo.usd")`', '') + asset badExpression = @`bogus_expression("foo.usd")`@ + + # Usd.Attribute.Get() returns Sdf.AssetPath(`foo_${THIS_VAR_MISSING}.usd`, '') + asset partialExpression = @`if(${SOME_TRUE_VALUE}, "foo_${THIS_VAR_MISSING}.usd")`@ + +This partial expression behavior allows substitution tokens to pass through Usd +for further evaluation by downstream clients. + +The same behavior described above applies for asset path-valued metadata +(including asset paths authored in dictionaries) retrieved via +:usdcpp:`UsdObject::GetMetadata`. + +Change Processing +----------------- + +Changes to stage variables that are used in expressions in references and other +composition arcs will trigger recomposition as needed, as much of the necessary +dependency tracking for this is already in place in Pcp. + +Changes that affect expressions in asset path-valued attributes and metadata +present a bigger problem. Ideally, these would cause :usdcpp:`UsdStage` to send +out a change notice for just the prims and properties that were +affected. However, since :usdcpp:`UsdStage` does not cache or pre-compute any of +these values, it currently has no way of determining exactly what objects on the +stage depend on a given stage variable. + +To work around this, when the composed value of a stage variable changes, +:usdcpp:`UsdStage` will send out a notice indicating a full resync of the stage, +even though internally nothing may actually be recomposed. This is a heavy +hammer but ensures that clients will be made aware that there was a possible +change to any asset path-valued attributes. :usdcpp:`UsdStage` already takes +this approach when dealing with "resolver changed" notices from Ar, which +indicate that any asset path that had previously been resolved might now resolve +to a new path (see here). + +An alternative solution is to provide a new Usd notice (or an extra bit on the +existing :usdcpp:`UsdNotice::ObjectsChanged` notice) that specifically indicates +that all asset path values may have changed on the associated change, instead of +sending out the full resync notice. This allows clients to choose the level of +invalidation they need to perform in response to this change, but it puts +clients on the hook for processing this new notice. + +Yet another alternative is to implement the caching for asset path-valued +attributes and metadata mentioned above. :usdcpp:`UsdStage` would keep track of +the asset paths it has handed out via :usdcpp:`UsdAttribute::Get` and other API +and use that to determine what asset paths were affected by a stage variable +change. This would allow the normal :usdcpp:`UsdNotice::ObjectsChanged` notice +to be sent instead of the coarse invalidation mentioned above. This approach +would fit into existing client code more easily, but would add extra overhead +and complexity to UsdStage when retrieving the asset path values. This overhead +should be limited to just asset path values, although some experimentation would +be needed to confirm this. + +Dependency Analysis / Asset Isolation +------------------------------------- + +Dependency analysis involves inspecting a given asset for all other assets it +refers to. This information is used by asset isolation to create a separate copy +of an asset in a different location that can be packaged up and distributed to +other locations while maintaining the same behaviors. In USD, this is primarily +handled by code like :usdcpp:`UsdUtilsComputeAllDependencies`. + +The analysis process recursively walks layers while looking at scene description +like composition arcs and attributes for references to other assets, which are +recorded as dependencies. This process gets complicated with expressions in the +mix, since the full set of referenced assets may not be enumerable just by +looking at the scene description. + +The simplest case is an asset where all of the stage variables referenced by +expressions in that asset are defined. In that case, every expression can be +evaluated and the resulting assets noted as explicit dependencies. For example, +consider something like: + +.. code-block:: usda + + #usda 1.0 + ( + stageVariables = { + string VAR = "x" + } + ) + + def "Asset" ( + references = @`if(eq(${VAR}, "x"), "x.usd" : "y.usd"`@ + ) + { + } + +In this case, dependency analysis can determine that this asset in its current +state depends only on :filename:`x.usd`, so asset isolation would just copy over +that file. That should be sufficient for cases where we only need to consume the +isolated asset as-is. + +But, what if we needed to work with the isolated asset fully and change VAR to +"y" sometimes? In that case, we'd need dependency analysis to recognize that +:filename:`y.usd` is a possible dependency and must also be captured. That is +possible in the example above, since the results of the "if" expression are +enumerable just by looking at the expression itself. But just a simple direct +substitution in an asset path makes this impossible: + +.. code-block:: usda + + #usda 1.0 + ( + stageVariables = { + string VAR = "x" + } + ) + + def "Asset" ( + references = @`${VAR}.usd`@ + ) + { + } + +In this case, the best that dependency analysis can do is note that +:filename:`x.usd` is a dependency – it has no idea that :filename:`y.usd` is a +possible value. + +Dependency analysis will make a "best effort" attempt at enumerating all +possible dependencies. This implies the ability to evaluate expressions in some +mode where all possible results are returned if possible. As an initial step, +for cases like variable substitutions where the possible results are not +enumerable, dependency analysis will issue a warning. + +In the future (or possibly as part of this work, if we deem it necessary), the +dependency analysis functions would allow users to supply a callback or plugin +that would be expected to list all of the possible results of a given +expression. We could provide a default callback implementation that relies on +filesystem operations and globbing. + +GUI Support +----------- + +:ref:`toolset:usdview` will be updated to display the evaluated expression (or +optionally the authored expression) in its UI for payload/references/sublayer +asset paths. + +For variant selections, the combo box that lets users pick a selection will be +disabled, but will display the evaluated expression. + +In both cases, we may want to add some kind of affordance to allow users to view +the expression itself. + +Example Use Cases +================= + +Variant Selections +------------------ + +Stage variable expressions can provide flexible ways of specifying variations +for logical groups of prims that are controlled via a single stage variable. + +For example, consider an asset with a large crowd of models, each with variants +controlling some aspect of their look, like a color palette. The crowds artist +could select models that should have the same color palette and set their +variant selections to corresponding stage variables, like: + +.. code-block:: usda + :caption: Crowd.usd + + #usda 1.0 + ( + stageVariables = { + string COLOR_GROUP_1 = "regular" + string COLOR_GROUP_2 = "regular" + } + ) + + def "Crowd" + { + def "Model_1" ( + variantSelection = { + string palette = "${COLOR_GROUP_1}" + } + ) + { + } + + def "Model_2" ( + variantSelection = { + string palette = "${COLOR_GROUP_2}" + } + ) + { + } + + # etc., etc. ... + } + +When an artist references this crowd asset into their own stage, they can +change the color palettes for all of the prims in the groups determined +by the original crowd artist just by overriding the stage variable: + +.. code-block:: usda + :caption: Shot.usd + + #usda 1.0 + ( + stageVariables = { + string COLOR_GROUP_1 = "rainbow" + string COLOR_GROUP_2 = "monochrome" + } + ) + + def "Crowd" (references = @Crowd.usd@) + { + } + +The artist can still change the palette variant on individual models if +they desire simply by overriding the variant selection on the specific prim. + +Varying Texture Format +---------------------- + +Disney Animation's Moana Island data set uses :filename:`.png` and +:filename:`.exr` files for environment maps by default. However, RenderMan +does not support these formats, so the data set provides an alternate root +layer that overrides the attributes specifying these maps with +:filename:`.tex` images instead. On one hand, this demonstrates the power +of USD's sparse overrides. On the other hand, it means these overrides +have to be kept in sync with the underlying data set -- if the namespace +location of the skydome prim changed, for example, the overrides would +need to be updated as well. + +With stage variable expressions, this difference could be encoded at the +asset path attribute itself: + +.. code-block:: usda + + asset inputs:texture:file = @`if(${FOR_PRMAN}, "../textures/islandsunEnv.tex", "../textures/islandsun.exr")@ + +The data set could still provide the RenderMan-specific root layer as an +entry point, but instead of overriding the texture attributes it would just +set the FOR_PRMAN stage variable to true. + +Render Passes +------------- + +One intended use case at Pixar is to encode a named "render pass" into a +stage variable and conditionally include sublayers based on the specified +render pass. For example, an artist could specify that they are rendering the +``fx`` render pass for a given shot via a stage variable. The shot's sublayer +list would include a special sublayer that deactivated non-effects-related +geometry to speed up processing. Shots might have many different render pass +layers for the artist to choose from, each of which would prune out geometry +or perform other overrides specific to that pass. This might look like: + +.. code-block:: usda + + #usda 1.0 + ( + subLayers = [ + @render_pass_${RENDER_PASS}.usd@, + ... + ] + ) + +Shot-Level Overrides in Sequences +--------------------------------- + +At Pixar, animation is organized into sequences (e.g. r720) that are groups of +shots (e.g. r720_1, r720_2, etc.). Sequences may contain tens of shots. The +scene description for each shot typically consists of a root layer for that +shot, which then includes a series of sublayers for various departments that are +specific to that shot, followed by a series of sublayers that are specific to +that sequence and shared among all of the constituent shots. Here's a very +simplified example: + ++-------------------+-------------------+ +| r720_1 layers | r720_2 layers | ++===================+===================+ +| r720_1.usd | r720_2.usd | ++-------------------+-------------------+ +| r720_1_anim.usd | r720_2_anim.usd | ++-------------------+-------------------+ +| r720_1_layout.usd | r720_2_layout.usd | ++-------------------+-------------------+ +| r720.usd | ++---------------------------------------+ +| r720_anim.usd | ++---------------------------------------+ +| r720_layout.usd | ++---------------------------------------+ + +During production, there are cases where the same set of overrides must +be authored on a subset of the shots in a sequence. We currently deal with +this by using a tool that iterates over the desired shots and applies the +edits to each one. Scene variable expressions could simplify this greatly +by authoring the edits into a sequence-level sublayer that is only included +for the desired shots. This might look like: + +.. code-block:: usda + :caption: r720_1.usd + + #usda 1.0 + ( + stageVariables = { + string SHOT = "01" + } + ) + + #... + +.. code-block:: usda + :caption: r720_2.usd + + #usda 1.0 + ( + stageVariables = { + string SHOT = "02" + } + ) + + #... + +.. code-block:: usda + :caption: r720.usd + + #usda 1.0 + ( + subLayers = [ + @`if(in(${SHOT}, ["01", "03"]), "r720_shot_edits.usd")`@, + @r720_anim.usd@, + ... + ] + ) + + #... diff --git a/extras/usd/tutorials/simpleShading/generate_simpleShading.py b/extras/usd/tutorials/simpleShading/generate_simpleShading.py index a45cdce5c2..60c65ede10 100644 --- a/extras/usd/tutorials/simpleShading/generate_simpleShading.py +++ b/extras/usd/tutorials/simpleShading/generate_simpleShading.py @@ -84,8 +84,7 @@ pbrShader.CreateInput("diffuseColor", Sdf.ValueTypeNames.Color3f).ConnectToSource(diffuseTextureSampler.ConnectableAPI(), 'rgb') # Now bind the Material to the card -binding = UsdShade.MaterialBindingAPI.Apply(billboard.GetPrim()) -if binding: - binding.Bind(material) +billboard.GetPrim().ApplyAPI(UsdShade.MaterialBindingAPI) +UsdShade.MaterialBindingAPI(billboard).Bind(material) stage.Save() diff --git a/pxr/CMakeLists.txt b/pxr/CMakeLists.txt index 952eefe852..00cafd3d98 100644 --- a/pxr/CMakeLists.txt +++ b/pxr/CMakeLists.txt @@ -21,6 +21,4 @@ install(FILES DESTINATION "${CMAKE_INSTALL_PREFIX}" ) -if (NOT PXR_BUILD_MONOLITHIC) - install(EXPORT pxrTargets DESTINATION "cmake") -endif() +install(EXPORT pxrTargets DESTINATION "cmake") diff --git a/pxr/base/arch/virtualMemory.cpp b/pxr/base/arch/virtualMemory.cpp index a04ae68b8a..4337b8c31e 100644 --- a/pxr/base/arch/virtualMemory.cpp +++ b/pxr/base/arch/virtualMemory.cpp @@ -35,6 +35,12 @@ PXR_NAMESPACE_OPEN_SCOPE +template +static inline T *RoundToPageAddr(T *addr) { + static uint64_t PAGEMASK = ~(static_cast(ArchGetPageSize())-1); + return reinterpret_cast(reinterpret_cast(addr) & PAGEMASK); +} + #if defined (ARCH_OS_WINDOWS) void * @@ -55,14 +61,27 @@ ArchFreeVirtualMemory(void *start, size_t /*numBytes*/) return VirtualFree(start, 0, MEM_RELEASE); } -#else // not ARCH_OS_WINDOWS, assume POSIX (mmap, mprotect) +bool +ArchSetMemoryProtection(void const *start, size_t numBytes, + ArchMemoryProtection protection) +{ + void *pageStart = RoundToPageAddr(const_cast(start)); + SIZE_T len = numBytes + (reinterpret_cast(start)- + reinterpret_cast(pageStart)); -template -static inline T *RoundToPageAddr(T *addr) { - static uint64_t PAGEMASK = ~(static_cast(ArchGetPageSize())-1); - return reinterpret_cast(reinterpret_cast(addr) & PAGEMASK); + DWORD protXlat[] = { + PAGE_NOACCESS, + PAGE_READONLY, + PAGE_READWRITE, // Unclear what the difference is btw + PAGE_WRITECOPY // READWRITE & WRITECOPY for private mappings... + }; + + DWORD oldProtect; + return VirtualProtect(pageStart, len, protXlat[protection], &oldProtect); } +#else // not ARCH_OS_WINDOWS, assume POSIX (mmap, mprotect) + void * ArchReserveVirtualMemory(size_t numBytes) { @@ -89,6 +108,25 @@ ArchFreeVirtualMemory(void *start, size_t numBytes) return munmap(start, numBytes) == 0; } +bool +ArchSetMemoryProtection(void const *start, size_t numBytes, + ArchMemoryProtection protection) +{ + void *pageStart = RoundToPageAddr(const_cast(start)); + size_t len = numBytes + (reinterpret_cast(start)- + reinterpret_cast(pageStart)); + + int protXlat[] = { + PROT_NONE, + PROT_READ, + PROT_READ | PROT_WRITE, // Yes these are the same on POSIX + PROT_READ | PROT_WRITE // + }; + + int result = mprotect(pageStart, len, protXlat[protection]); + return result == 0; +} + #endif // POSIX PXR_NAMESPACE_CLOSE_SCOPE diff --git a/pxr/base/arch/virtualMemory.h b/pxr/base/arch/virtualMemory.h index 6c917b1bf0..d99371ca7f 100644 --- a/pxr/base/arch/virtualMemory.h +++ b/pxr/base/arch/virtualMemory.h @@ -57,6 +57,25 @@ ArchCommitVirtualMemoryRange(void *start, size_t numBytes); ARCH_API bool ArchFreeVirtualMemory(void *start, size_t numBytes); +/// Memory protection options, see ArchSetMemoryProtection(). +enum ArchMemoryProtection { + ArchProtectNoAccess, + ArchProtectReadOnly, + ArchProtectReadWrite, + ArchProtectReadWriteCopy +}; + +/// Change the memory protection on the pages containing \p start and \p start + +/// \p numBytes to \p protection. Return true if the protection is changed +/// successfully. Return false in case of an error; check errno. This function +/// rounds \p start to the nearest lower page boundary. On POSIX systems, +/// ArchProtectReadWrite and ArchProtectReadWriteCopy are the same, on Windows +/// they differ but the Windows API documentation does not make it clear what +/// using ReadWrite means for a private file-backed mapping. +ARCH_API bool +ArchSetMemoryProtection(void const *start, size_t numBytes, + ArchMemoryProtection protection); + PXR_NAMESPACE_CLOSE_SCOPE #endif // PXR_BASE_ARCH_VIRTUAL_MEMORY_H diff --git a/pxr/base/gf/frustum.cpp b/pxr/base/gf/frustum.cpp index 732379e035..cd7d978ed8 100644 --- a/pxr/base/gf/frustum.cpp +++ b/pxr/base/gf/frustum.cpp @@ -689,46 +689,58 @@ GfFrustum::ComputeCornersAtDistance(double d) const return corners; } -GfFrustum -GfFrustum::ComputeNarrowedFrustum(const GfVec2d &point, - const GfVec2d &halfSize) const +// Utility function for mapping normalized window coordinates to a window point. +static GfVec2d +_WindowNormalizedToPoint(const GfVec2d &windowPos, const GfRange2d &windowRect) { // Map the point from normalized space (-1 to 1) onto the frustum's // window. First, convert the point into the range from 0 to 1, // then interpolate in the window rectangle. - GfVec2d scaledPoint = .5 * (GfVec2d(1.0, 1.0) + point); - GfVec2d windowPoint = _window.GetMin() + GfCompMult(scaledPoint, - _window.GetSize()); + const GfVec2d scaledPos = .5 * (GfVec2d(1.0, 1.0) + windowPos); + return windowRect.GetMin() + GfCompMult(scaledPos, windowRect.GetSize()); +} - return _ComputeNarrowedFrustumSub(windowPoint, halfSize); +GfFrustum +GfFrustum::ComputeNarrowedFrustum(const GfVec2d &windowPos, + const GfVec2d &size) const +{ + const GfVec2d windowPoint = _WindowNormalizedToPoint(windowPos, _window); + + return _ComputeNarrowedFrustumSub(windowPoint, size); } GfFrustum GfFrustum::ComputeNarrowedFrustum(const GfVec3d &worldPoint, - const GfVec2d &halfSize) const + const GfVec2d &size) const { // Map the point from worldspace onto the frustum's window - GfVec3d lclPt = ComputeViewMatrix().Transform(worldPoint); - if (lclPt[2] >= 0) { + GfVec3d camSpacePoint = ComputeViewMatrix().Transform(worldPoint); + if (camSpacePoint[2] >= 0) { TF_WARN("Given worldPoint is behind or at the eye"); // Start with this frustum return *this; } - double scaleFactor = _nearFar.GetMin() / -lclPt[2]; - GfVec2d windowPoint(lclPt[0] * scaleFactor, lclPt[1] * scaleFactor); - return _ComputeNarrowedFrustumSub(windowPoint, halfSize); + GfVec2d windowPoint(camSpacePoint[0], camSpacePoint[1]); + if (_projectionType == Perspective) { + // project the camera space point to the reference plane (-1 to 1) + // XXX Note: If we ever allow reference plane depth to be other + // than 1.0, we'll need to revisit this. + windowPoint /= -camSpacePoint[2]; + } + + return _ComputeNarrowedFrustumSub(windowPoint, size); } GfFrustum GfFrustum::_ComputeNarrowedFrustumSub(const GfVec2d windowPoint, - const GfVec2d &halfSize) const + const GfVec2d &size) const { // Start with this frustum GfFrustum narrowedFrustum = *this; // Also convert the sizes. - GfVec2d halfSizeOnRefPlane = .5 * GfCompMult(halfSize, _window.GetSize()); + GfVec2d halfSizeOnRefPlane = .5 * GfCompMult(size, _window.GetSize()); // Shrink the narrowed frustum's window to surround the point. GfVec2d min = windowPoint - halfSizeOnRefPlane; @@ -750,28 +762,13 @@ GfFrustum::_ComputeNarrowedFrustumSub(const GfVec2d windowPoint, return narrowedFrustum; } -// Utility function for mapping an input value from -// one range to another. -static double -_Rescale(double in, - double inA, double inB, - double outA, double outB ) -{ - double factor = (inA==inB) ? 0.0 : ((inA-in) / (inA-inB)); - return outA + ((outB-outA)*factor); -} - static GfRay _ComputeUntransformedRay(GfFrustum::ProjectionType projectionType, const GfRange2d &window, const GfVec2d &windowPos, const double nearDist) { - // Compute position on window, from provided normalized - // (-1 to 1) coordinates. - double winX = _Rescale(windowPos[0], -1.0, 1.0, - window.GetMin()[0], window.GetMax()[0]); - double winY = _Rescale(windowPos[1], -1.0, 1.0, - window.GetMin()[1], window.GetMax()[1]); + const GfVec2d windowPoint = _WindowNormalizedToPoint(windowPos, window); + // Compute the camera-space starting point (the viewpoint) and // direction (toward the point on the window). @@ -781,10 +778,10 @@ static GfRay _ComputeUntransformedRay(GfFrustum::ProjectionType projectionType, // Note that the ray is starting at the origin and not // the near plane. pos = GfVec3d(0); - dir = GfVec3d(winX, winY, -1.0).GetNormalized(); + dir = GfVec3d(windowPoint[0], windowPoint[1], -1.0).GetNormalized(); } else { - pos.Set(winX, winY, -nearDist); + pos.Set(windowPoint[0], windowPoint[1], -nearDist); dir = -GfVec3d::ZAxis(); } diff --git a/pxr/base/gf/frustum.h b/pxr/base/gf/frustum.h index 5121bd7c01..551be1bb22 100644 --- a/pxr/base/gf/frustum.h +++ b/pxr/base/gf/frustum.h @@ -513,37 +513,46 @@ class GfFrustum { GF_API std::vector ComputeCornersAtDistance(double d) const; - /// Returns a frustum that is a narrowed-down version of this frustum, - /// such that the frustum rectangle on the near plane encloses \p point - /// with at most \p halfSize[0] distance on the left and right and at most - /// \p halfSize[1] distance on the top and bottom. (If \p point is closer - /// than the half size to a side of the frustum, that side is left alone. - /// The point and sizes are in normalized 2D coordinates; they range from - /// (-1, -1) at the lower left corner of the near-plane window rectangle - /// to (1,1) at the upper right corner. + /// Returns a frustum that is a narrowed-down version of this frustum. The + /// new frustum has the same near and far planes, but the other planes are + /// adjusted to be centered on \p windowPos with the new width and height + /// obtained from the existing width and height by multiplying by \p size[0] + /// and \p size[1], respectively. Finally, the new frustum is clipped + /// against this frustum so that it is completely contained in the existing + /// frustum. + /// + /// \p windowPos is given in normalized coords (-1 to +1 in both dimensions). + /// \p size is given as a scalar (0 to 1 in both dimensions). /// - /// \p point is a 2d point expressed as a normalized window position. + /// If the \p windowPos or \p size given is outside these ranges, it may + /// result in returning a collapsed frustum. /// /// This method is useful for computing a volume to use for interactive /// picking. - GF_API GfFrustum ComputeNarrowedFrustum(const GfVec2d &point, - const GfVec2d &halfSize) const; - - /// Returns a frustum that is a narrowed-down version of this frustum, - /// such that the frustum rectangle on the near plane encloses \p point - /// with at most \p halfSize[0] distance on the left and right and at most - /// \p halfSize[1] distance on the top and bottom. (If \p point is closer - /// than the half size to a side of the frustum, that side is left alone. - /// The point and sizes are in normalized 2D coordinates; they range from - /// (-1, -1) at the lower left corner of the near-plane window rectangle - /// to (1,1) at the upper right corner. + GF_API GfFrustum ComputeNarrowedFrustum(const GfVec2d &windowPos, + const GfVec2d &size) const; + + /// Returns a frustum that is a narrowed-down version of this frustum. The + /// new frustum has the same near and far planes, but the other planes are + /// adjusted to be centered on \p worldPoint with the new width and height + /// obtained from the existing width and height by multiplying by \p size[0] + /// and \p size[1], respectively. Finally, the new frustum is clipped + /// against this frustum so that it is completely contained in the existing + /// frustum. /// - /// \p point is a 3d point expressed in world coordinates + /// \p worldPoint is given in world space coordinates. + /// \p size is given as a scalar (0 to 1 in both dimensions). + /// + /// If the \p size given is outside this range, it may result in returning + /// a collapsed frustum. + /// + /// If the \p worldPoint is at or behind the eye of the frustum, it will + /// return a frustum equal to this frustum. /// /// This method is useful for computing a volume to use for interactive /// picking. GF_API GfFrustum ComputeNarrowedFrustum(const GfVec3d &worldPoint, - const GfVec2d &halfSize) const; + const GfVec2d &size) const; /// Builds and returns a \c GfRay that starts at the viewpoint and extends /// through the given \a windowPos given in normalized coords (-1 to +1 in @@ -633,21 +642,24 @@ class GfFrustum { const GfVec3d &camSpaceFrom, const GfVec3d &camSpaceDir) const; - // Returns a frustum that is a narrowed-down version of this frustum, such - // that the frustum rectangle on the near plane encloses \p point with at - // most \p halfSize[0] distance on the left and right and at most \p - // halfSize[1] distance on the top and bottom. (If \p point is closer than - // the half size to a side of the frustum, that side is left alone. The - // point and sizes are in normalized 2D coordinates; they range from (-1, - // -1) at the lower left corner of the near-plane window rectangle to - // (1,1) at the upper right corner. + // Returns a frustum that is a narrowed-down version of this frustum. The + // new frustum has the same near and far planes, but the other planes are + // adjusted to be centered on \p windowPoint with the new width and height + // obtained from the existing width and height by multiplying by \p size[0] + // and \p size[1], respectively. Finally, the new frustum is clipped + // against this frustum so that it is completely contained in the existing + // frustum. + // + // \p windowPoint is given in window coordinates. + // \p size is given as a scalar (0 to 1 in both dimensions). // - // \p windowPoint is expressed in window coordinates + // If the \p size given is outside this range, it may result in returning + // a collapsed frustum. // // This method is useful for computing a volume to use for interactive // picking. GfFrustum _ComputeNarrowedFrustumSub(const GfVec2d windowPoint, - const GfVec2d &halfSize) const; + const GfVec2d &size) const; bool _SegmentIntersects(GfVec3d const &p0, uint32_t p0Mask, GfVec3d const &p1, uint32_t p1Mask) const; diff --git a/pxr/base/gf/testenv/testGfFrustum.py b/pxr/base/gf/testenv/testGfFrustum.py index 095980ea93..311a40dfbb 100644 --- a/pxr/base/gf/testenv/testGfFrustum.py +++ b/pxr/base/gf/testenv/testGfFrustum.py @@ -234,6 +234,19 @@ def test_ComputeNarrowedFrustum(self): self.assertTrue(Gf.IsClose(narrowF.window.min, Gf.Vec2d(-0.3, -0.2), 0.0001)) self.assertTrue(Gf.IsClose(narrowF.window.max, Gf.Vec2d(0.3, 0.2), 0.0001)) + narrowF = f.ComputeNarrowedFrustum(Gf.Vec3d(.1, .2, -5), Gf.Vec2d(0.1, 0.1)) + self.assertTrue(Gf.IsClose(narrowF.window.min, Gf.Vec2d(-0.2, 0.0), 0.0001)) + self.assertTrue(Gf.IsClose(narrowF.window.max, Gf.Vec2d(0.4, 0.4), 0.0001)) + + f.projectionType = f.Perspective + narrowF = f.ComputeNarrowedFrustum(Gf.Vec3d(0, 0, -1), Gf.Vec2d(0.1, 0.1)) + self.assertTrue(Gf.IsClose(narrowF.window.min, Gf.Vec2d(-0.3, -0.2), 0.0001)) + self.assertTrue(Gf.IsClose(narrowF.window.max, Gf.Vec2d(0.3, 0.2), 0.0001)) + + narrowF = f.ComputeNarrowedFrustum(Gf.Vec3d(.1, .2, -5), Gf.Vec2d(0.1, 0.1)) + self.assertTrue(Gf.IsClose(narrowF.window.min, Gf.Vec2d(-0.28, -0.16), 0.0001)) + self.assertTrue(Gf.IsClose(narrowF.window.max, Gf.Vec2d(0.32, 0.24), 0.0001)) + # Given a point behind the eye should get the same frustum back narrowF = f.ComputeNarrowedFrustum(Gf.Vec3d(0, 0, 1), Gf.Vec2d(0.1, 0.1)) self.assertTrue(Gf.IsClose(narrowF.window.min, Gf.Vec2d(-3.0,-2.0), 0.0001)) diff --git a/pxr/base/gf/wrapBBox3d.cpp b/pxr/base/gf/wrapBBox3d.cpp index 185c86e737..823063150e 100644 --- a/pxr/base/gf/wrapBBox3d.cpp +++ b/pxr/base/gf/wrapBBox3d.cpp @@ -132,5 +132,9 @@ void wrapBBox3d() ; to_python_converter, TfPySequenceToPython > >(); - + + // Allow conversion of lists of GfBBox3d to std::vector + TfPyContainerConversions::from_python_sequence< + std::vector, + TfPyContainerConversions::variable_capacity_policy >(); } diff --git a/pxr/base/gf/wrapVec.template.cpp b/pxr/base/gf/wrapVec.template.cpp index bea3321c90..6eb236db9a 100644 --- a/pxr/base/gf/wrapVec.template.cpp +++ b/pxr/base/gf/wrapVec.template.cpp @@ -253,7 +253,7 @@ static list __getslice__(const {{ VEC }} &self, slice indices) { // name should be "get_indices". // bounds = indices.get_indicies<>(begin, end); - } catch (std::invalid_argument) { + } catch (std::invalid_argument const &) { return result; } @@ -313,7 +313,7 @@ static void __setslice__({{ VEC }} &self, slice indices, object values) { // name should be "get_indices". // bounds = indices.get_indicies<>(begin, end); - } catch (std::invalid_argument) { + } catch (std::invalid_argument const &) { sliceLength = 0; } diff --git a/pxr/base/gf/wrapVec2d.cpp b/pxr/base/gf/wrapVec2d.cpp index 9c0b9952ff..64ab600151 100644 --- a/pxr/base/gf/wrapVec2d.cpp +++ b/pxr/base/gf/wrapVec2d.cpp @@ -224,7 +224,7 @@ static list __getslice__(const GfVec2d &self, slice indices) { // name should be "get_indices". // bounds = indices.get_indicies<>(begin, end); - } catch (std::invalid_argument) { + } catch (std::invalid_argument const &) { return result; } @@ -284,7 +284,7 @@ static void __setslice__(GfVec2d &self, slice indices, object values) { // name should be "get_indices". // bounds = indices.get_indicies<>(begin, end); - } catch (std::invalid_argument) { + } catch (std::invalid_argument const &) { sliceLength = 0; } diff --git a/pxr/base/gf/wrapVec2f.cpp b/pxr/base/gf/wrapVec2f.cpp index c648fb92ce..0b14c3fffd 100644 --- a/pxr/base/gf/wrapVec2f.cpp +++ b/pxr/base/gf/wrapVec2f.cpp @@ -224,7 +224,7 @@ static list __getslice__(const GfVec2f &self, slice indices) { // name should be "get_indices". // bounds = indices.get_indicies<>(begin, end); - } catch (std::invalid_argument) { + } catch (std::invalid_argument const &) { return result; } @@ -284,7 +284,7 @@ static void __setslice__(GfVec2f &self, slice indices, object values) { // name should be "get_indices". // bounds = indices.get_indicies<>(begin, end); - } catch (std::invalid_argument) { + } catch (std::invalid_argument const &) { sliceLength = 0; } diff --git a/pxr/base/gf/wrapVec2h.cpp b/pxr/base/gf/wrapVec2h.cpp index 7e084ea5cc..473ef4eb27 100644 --- a/pxr/base/gf/wrapVec2h.cpp +++ b/pxr/base/gf/wrapVec2h.cpp @@ -224,7 +224,7 @@ static list __getslice__(const GfVec2h &self, slice indices) { // name should be "get_indices". // bounds = indices.get_indicies<>(begin, end); - } catch (std::invalid_argument) { + } catch (std::invalid_argument const &) { return result; } @@ -284,7 +284,7 @@ static void __setslice__(GfVec2h &self, slice indices, object values) { // name should be "get_indices". // bounds = indices.get_indicies<>(begin, end); - } catch (std::invalid_argument) { + } catch (std::invalid_argument const &) { sliceLength = 0; } diff --git a/pxr/base/gf/wrapVec2i.cpp b/pxr/base/gf/wrapVec2i.cpp index ab7b9e81df..d9955db45c 100644 --- a/pxr/base/gf/wrapVec2i.cpp +++ b/pxr/base/gf/wrapVec2i.cpp @@ -204,7 +204,7 @@ static list __getslice__(const GfVec2i &self, slice indices) { // name should be "get_indices". // bounds = indices.get_indicies<>(begin, end); - } catch (std::invalid_argument) { + } catch (std::invalid_argument const &) { return result; } @@ -264,7 +264,7 @@ static void __setslice__(GfVec2i &self, slice indices, object values) { // name should be "get_indices". // bounds = indices.get_indicies<>(begin, end); - } catch (std::invalid_argument) { + } catch (std::invalid_argument const &) { sliceLength = 0; } diff --git a/pxr/base/gf/wrapVec3d.cpp b/pxr/base/gf/wrapVec3d.cpp index 5880148a29..59492e582f 100644 --- a/pxr/base/gf/wrapVec3d.cpp +++ b/pxr/base/gf/wrapVec3d.cpp @@ -247,7 +247,7 @@ static list __getslice__(const GfVec3d &self, slice indices) { // name should be "get_indices". // bounds = indices.get_indicies<>(begin, end); - } catch (std::invalid_argument) { + } catch (std::invalid_argument const &) { return result; } @@ -307,7 +307,7 @@ static void __setslice__(GfVec3d &self, slice indices, object values) { // name should be "get_indices". // bounds = indices.get_indicies<>(begin, end); - } catch (std::invalid_argument) { + } catch (std::invalid_argument const &) { sliceLength = 0; } diff --git a/pxr/base/gf/wrapVec3f.cpp b/pxr/base/gf/wrapVec3f.cpp index 32dfd58aab..882c401ca6 100644 --- a/pxr/base/gf/wrapVec3f.cpp +++ b/pxr/base/gf/wrapVec3f.cpp @@ -247,7 +247,7 @@ static list __getslice__(const GfVec3f &self, slice indices) { // name should be "get_indices". // bounds = indices.get_indicies<>(begin, end); - } catch (std::invalid_argument) { + } catch (std::invalid_argument const &) { return result; } @@ -307,7 +307,7 @@ static void __setslice__(GfVec3f &self, slice indices, object values) { // name should be "get_indices". // bounds = indices.get_indicies<>(begin, end); - } catch (std::invalid_argument) { + } catch (std::invalid_argument const &) { sliceLength = 0; } diff --git a/pxr/base/gf/wrapVec3h.cpp b/pxr/base/gf/wrapVec3h.cpp index ca20ce49a3..75249ca2af 100644 --- a/pxr/base/gf/wrapVec3h.cpp +++ b/pxr/base/gf/wrapVec3h.cpp @@ -247,7 +247,7 @@ static list __getslice__(const GfVec3h &self, slice indices) { // name should be "get_indices". // bounds = indices.get_indicies<>(begin, end); - } catch (std::invalid_argument) { + } catch (std::invalid_argument const &) { return result; } @@ -307,7 +307,7 @@ static void __setslice__(GfVec3h &self, slice indices, object values) { // name should be "get_indices". // bounds = indices.get_indicies<>(begin, end); - } catch (std::invalid_argument) { + } catch (std::invalid_argument const &) { sliceLength = 0; } diff --git a/pxr/base/gf/wrapVec3i.cpp b/pxr/base/gf/wrapVec3i.cpp index ac77ff7cd0..710cae355f 100644 --- a/pxr/base/gf/wrapVec3i.cpp +++ b/pxr/base/gf/wrapVec3i.cpp @@ -204,7 +204,7 @@ static list __getslice__(const GfVec3i &self, slice indices) { // name should be "get_indices". // bounds = indices.get_indicies<>(begin, end); - } catch (std::invalid_argument) { + } catch (std::invalid_argument const &) { return result; } @@ -264,7 +264,7 @@ static void __setslice__(GfVec3i &self, slice indices, object values) { // name should be "get_indices". // bounds = indices.get_indicies<>(begin, end); - } catch (std::invalid_argument) { + } catch (std::invalid_argument const &) { sliceLength = 0; } diff --git a/pxr/base/gf/wrapVec4d.cpp b/pxr/base/gf/wrapVec4d.cpp index e901eecbba..2674ae27c1 100644 --- a/pxr/base/gf/wrapVec4d.cpp +++ b/pxr/base/gf/wrapVec4d.cpp @@ -224,7 +224,7 @@ static list __getslice__(const GfVec4d &self, slice indices) { // name should be "get_indices". // bounds = indices.get_indicies<>(begin, end); - } catch (std::invalid_argument) { + } catch (std::invalid_argument const &) { return result; } @@ -284,7 +284,7 @@ static void __setslice__(GfVec4d &self, slice indices, object values) { // name should be "get_indices". // bounds = indices.get_indicies<>(begin, end); - } catch (std::invalid_argument) { + } catch (std::invalid_argument const &) { sliceLength = 0; } diff --git a/pxr/base/gf/wrapVec4f.cpp b/pxr/base/gf/wrapVec4f.cpp index 0a1ceaea00..24bfb7491f 100644 --- a/pxr/base/gf/wrapVec4f.cpp +++ b/pxr/base/gf/wrapVec4f.cpp @@ -224,7 +224,7 @@ static list __getslice__(const GfVec4f &self, slice indices) { // name should be "get_indices". // bounds = indices.get_indicies<>(begin, end); - } catch (std::invalid_argument) { + } catch (std::invalid_argument const &) { return result; } @@ -284,7 +284,7 @@ static void __setslice__(GfVec4f &self, slice indices, object values) { // name should be "get_indices". // bounds = indices.get_indicies<>(begin, end); - } catch (std::invalid_argument) { + } catch (std::invalid_argument const &) { sliceLength = 0; } diff --git a/pxr/base/gf/wrapVec4h.cpp b/pxr/base/gf/wrapVec4h.cpp index 7d095f1205..3969e19287 100644 --- a/pxr/base/gf/wrapVec4h.cpp +++ b/pxr/base/gf/wrapVec4h.cpp @@ -224,7 +224,7 @@ static list __getslice__(const GfVec4h &self, slice indices) { // name should be "get_indices". // bounds = indices.get_indicies<>(begin, end); - } catch (std::invalid_argument) { + } catch (std::invalid_argument const &) { return result; } @@ -284,7 +284,7 @@ static void __setslice__(GfVec4h &self, slice indices, object values) { // name should be "get_indices". // bounds = indices.get_indicies<>(begin, end); - } catch (std::invalid_argument) { + } catch (std::invalid_argument const &) { sliceLength = 0; } diff --git a/pxr/base/gf/wrapVec4i.cpp b/pxr/base/gf/wrapVec4i.cpp index a68fbceb05..c87c840405 100644 --- a/pxr/base/gf/wrapVec4i.cpp +++ b/pxr/base/gf/wrapVec4i.cpp @@ -204,7 +204,7 @@ static list __getslice__(const GfVec4i &self, slice indices) { // name should be "get_indices". // bounds = indices.get_indicies<>(begin, end); - } catch (std::invalid_argument) { + } catch (std::invalid_argument const &) { return result; } @@ -264,7 +264,7 @@ static void __setslice__(GfVec4i &self, slice indices, object values) { // name should be "get_indices". // bounds = indices.get_indicies<>(begin, end); - } catch (std::invalid_argument) { + } catch (std::invalid_argument const &) { sliceLength = 0; } diff --git a/pxr/base/tf/diagnosticBase.cpp b/pxr/base/tf/diagnosticBase.cpp index e32e2ce9ef..e0c70eeb53 100644 --- a/pxr/base/tf/diagnosticBase.cpp +++ b/pxr/base/tf/diagnosticBase.cpp @@ -51,12 +51,6 @@ TfDiagnosticBase::TfDiagnosticBase( _codeString = TfSafeString(codeString); } -string -TfDiagnosticBase::GetPrettyPrintString() const -{ - return _commentary; -} - bool TfDiagnosticBase::IsFatal() const { diff --git a/pxr/base/tf/diagnosticBase.h b/pxr/base/tf/diagnosticBase.h index e70c45a5b5..c14d416909 100644 --- a/pxr/base/tf/diagnosticBase.h +++ b/pxr/base/tf/diagnosticBase.h @@ -27,6 +27,7 @@ /// \file tf/diagnosticBase.h #include "pxr/pxr.h" +#include "pxr/base/tf/api.h" #include "pxr/base/tf/callContext.h" #include "pxr/base/tf/enum.h" #include "pxr/base/tf/refBase.h" @@ -178,17 +179,17 @@ class TfDiagnosticBase { return _quiet; } - /// Return the commentary string. - std::string GetPrettyPrintString() const; - /// Return true if this diagnostic's code is a fatal code. + TF_API bool IsFatal() const; /// Return true if this diagnostic's code is either a fatal or nonfatal /// coding error. + TF_API bool IsCodingError() const; /// Construct an instance. + TF_API TfDiagnosticBase(TfEnum code, char const *codeString, TfCallContext const &context, const std::string& commentary, diff --git a/pxr/base/tf/diagnosticHelper.cpp b/pxr/base/tf/diagnosticHelper.cpp index 9cc3d48b12..2e26c3806c 100644 --- a/pxr/base/tf/diagnosticHelper.cpp +++ b/pxr/base/tf/diagnosticHelper.cpp @@ -37,7 +37,7 @@ using std::string; PXR_NAMESPACE_OPEN_SCOPE // Helper functions for posting an error with TF_ERROR. -bool +void Tf_PostErrorHelper( const TfCallContext &context, const TfEnum &code, @@ -45,19 +45,18 @@ Tf_PostErrorHelper( { TfDiagnosticMgr::ErrorHelper(context, code, TfEnum::GetName(code).c_str()).Post(msg); - return false; } -bool +void Tf_PostErrorHelper( const TfCallContext &context, TfDiagnosticType code, const std::string &msg) { - return Tf_PostErrorHelper(context, TfEnum(code), msg); + Tf_PostErrorHelper(context, TfEnum(code), msg); } -bool +void Tf_PostErrorHelper( const TfCallContext &context, const TfEnum &code, @@ -67,10 +66,9 @@ Tf_PostErrorHelper( va_start(ap, fmt); Tf_PostErrorHelper(context, code, TfVStringPrintf(fmt, ap)); va_end(ap); - return false; } -bool +void Tf_PostErrorHelper( const TfCallContext &context, TfDiagnosticType code, @@ -80,10 +78,9 @@ Tf_PostErrorHelper( va_start(ap, fmt); Tf_PostErrorHelper(context, code, TfVStringPrintf(fmt, ap)); va_end(ap); - return false; } -bool +void Tf_PostQuietlyErrorHelper( const TfCallContext &context, const TfEnum &code, @@ -91,10 +88,9 @@ Tf_PostQuietlyErrorHelper( { TfDiagnosticMgr::ErrorHelper(context, code, TfEnum::GetName(code).c_str()).PostQuietly(msg); - return false; } -bool +void Tf_PostQuietlyErrorHelper( const TfCallContext &context, const TfEnum &code, @@ -103,10 +99,9 @@ Tf_PostQuietlyErrorHelper( { TfDiagnosticMgr::ErrorHelper(context, code, TfEnum::GetName(code).c_str()).PostQuietly(msg, info); - return false; } -bool +void Tf_PostQuietlyErrorHelper( const TfCallContext &context, const TfEnum &code, @@ -116,10 +111,9 @@ Tf_PostQuietlyErrorHelper( va_start(ap, fmt); Tf_PostQuietlyErrorHelper(context, code, TfVStringPrintf(fmt, ap)); va_end(ap); - return false; } -bool +void Tf_PostQuietlyErrorHelper( const TfCallContext &context, const TfEnum &code, @@ -130,10 +124,9 @@ Tf_PostQuietlyErrorHelper( va_start(ap, fmt); Tf_PostQuietlyErrorHelper(context, code, info, TfVStringPrintf(fmt, ap)); va_end(ap); - return false; } -bool +void Tf_PostErrorHelper( const TfCallContext &context, const TfDiagnosticInfo &info, @@ -142,10 +135,9 @@ Tf_PostErrorHelper( { TfDiagnosticMgr::ErrorHelper(context, code, TfEnum::GetName(code).c_str()).PostWithInfo(msg, info); - return false; } -bool +void Tf_PostErrorHelper( const TfCallContext &context, const TfDiagnosticInfo &info, @@ -156,7 +148,6 @@ Tf_PostErrorHelper( Tf_PostErrorHelper(context, info, code, TfVStringPrintf(fmt, ap)); va_end(ap); - return false; } // Helper functions for posting a warning with TF_WARN without a diagnostic code diff --git a/pxr/base/tf/diagnosticHelper.h b/pxr/base/tf/diagnosticHelper.h index 42c69ae5fe..f4c6b1592a 100644 --- a/pxr/base/tf/diagnosticHelper.h +++ b/pxr/base/tf/diagnosticHelper.h @@ -43,65 +43,65 @@ enum TfDiagnosticType : int; class TfEnum; class TfError; -TF_API bool +TF_API void Tf_PostErrorHelper( const TfCallContext &context, const TfEnum &code, const std::string &msg); -TF_API bool +TF_API void Tf_PostErrorHelper( const TfCallContext &context, TfDiagnosticType code, const std::string &msg); -TF_API bool +TF_API void Tf_PostErrorHelper( const TfCallContext &context, const TfEnum &code, const char *fmt, ...) ARCH_PRINTF_FUNCTION(3, 4); -TF_API bool +TF_API void Tf_PostErrorHelper( const TfCallContext &context, TfDiagnosticType code, const char *fmt, ...) ARCH_PRINTF_FUNCTION(3, 4); -TF_API bool +TF_API void Tf_PostErrorHelper( const TfCallContext &context, const TfDiagnosticInfo &info, const TfEnum &code, const std::string &msg); -TF_API bool +TF_API void Tf_PostErrorHelper( const TfCallContext &context, const TfDiagnosticInfo &info, const TfEnum &code, const char *fmt, ...) ARCH_PRINTF_FUNCTION(4, 5); -TF_API bool +TF_API void Tf_PostQuietlyErrorHelper( const TfCallContext &context, const TfEnum &code, const TfDiagnosticInfo &info, const std::string &msg); -TF_API bool +TF_API void Tf_PostQuietlyErrorHelper( const TfCallContext &context, const TfEnum &code, const TfDiagnosticInfo &info, const char *fmt, ...) ARCH_PRINTF_FUNCTION(4, 5); -TF_API bool +TF_API void Tf_PostQuietlyErrorHelper( const TfCallContext &context, const TfEnum &code, const std::string &msg); -TF_API bool +TF_API void Tf_PostQuietlyErrorHelper( const TfCallContext &context, const TfEnum &code, diff --git a/pxr/base/tf/functionRef.h b/pxr/base/tf/functionRef.h index 6b2a2ce3d2..ed51614d7c 100644 --- a/pxr/base/tf/functionRef.h +++ b/pxr/base/tf/functionRef.h @@ -98,9 +98,17 @@ class TfFunctionRef; template class TfFunctionRef { + // Type trait to detect when an argument is a potentially cv-qualified + // TfFunctionRef. This is used to disable the generic constructor and + // assignment operator so that TfFunctionRef arguments are copied rather + // than forming TfFunctionRefs pointing to TfFunctionRefs. + template + using _IsFunctionRef = std::is_same< + std::remove_cv_t>, TfFunctionRef>; + public: /// Construct with an lvalue callable \p fn. - template + template ::value>> constexpr TfFunctionRef(Fn &fn) noexcept : _fn(static_cast(std::addressof(fn))) , _invoke(_InvokeFn) {} @@ -116,7 +124,8 @@ class TfFunctionRef /// Assign from an lvalue callable \p fn. template - TfFunctionRef & + std::enable_if_t::value, + TfFunctionRef &> operator=(Fn &fn) noexcept { *this = TfFunctionRef(fn); return *this; diff --git a/pxr/base/tf/pyInterpreter.cpp b/pxr/base/tf/pyInterpreter.cpp index d6657915dc..84b55ecbf2 100644 --- a/pxr/base/tf/pyInterpreter.cpp +++ b/pxr/base/tf/pyInterpreter.cpp @@ -67,6 +67,9 @@ TfPyInitialize() if (!Py_IsInitialized()) { + // Starting with Python 3.7, the GIL is initialized as part of + // Py_Initialize(). Python 3.9 deprecated explicit GIL initialization. +#if PY_VERSION_HEX < 0x03070000 if (!ArchIsMainThread() && !PyEval_ThreadsInitialized()) { // Python claims that PyEval_InitThreads "should be called in the // main thread before creating a second thread or engaging in any @@ -74,6 +77,7 @@ TfPyInitialize() TF_WARN("Calling PyEval_InitThreads() for the first time outside " "the 'main thread'. Python doc says not to do this."); } +#endif const std::string s = ArchGetExecutablePath(); @@ -113,9 +117,10 @@ TfPyInitialize() sigaction(SIGINT, &origSigintHandler, NULL); #endif -#if PY_MAJOR_VERSION > 2 - // In python 3 PyEval_InitThreads must be called after Py_Initialize() - // see https://docs.python.org/3/c-api/init.html +#if PY_MAJOR_VERSION == 3 && PY_VERSION_HEX < 0x03070000 + // In Python 3 (before 3.7), PyEval_InitThreads must be called + // after Py_Initialize(). + // see https://docs.python.org/3/c-api/init.html#c.PyEval_InitThreads // // Initialize Python threading. This grabs the GIL. We'll release it // at the end of this function. diff --git a/pxr/base/tf/pyModule.cpp b/pxr/base/tf/pyModule.cpp index 23b240bdcc..d5bb2d284a 100644 --- a/pxr/base/tf/pyModule.cpp +++ b/pxr/base/tf/pyModule.cpp @@ -160,7 +160,7 @@ class Tf_ModuleProcessor { , _fileName(fileName) {} - handle<> operator()(tuple const &args, dict const &kw) const { + PyObject *operator()(PyObject *args, PyObject *kw) const { // Fabricate a python tracing event to record the python -> c++ -> // python transition. @@ -178,8 +178,7 @@ class Tf_ModuleProcessor { TfErrorMark m; // Call the function. - handle<> ret(allow_null( - PyObject_Call(_fn.ptr(), args.ptr(), kw.ptr()))); + PyObject *ret = PyObject_Call(_fn.ptr(), args, kw); // Fabricate the return tracing event. info.what = PyTrace_RETURN; @@ -196,6 +195,7 @@ class Tf_ModuleProcessor { // errors occurred, and if so, convert them to python exceptions. if (ARCH_UNLIKELY(!m.IsClean() && TfPyConvertTfErrorsToPythonException(m))) { + Py_DECREF(ret); throw_error_already_set(); } @@ -229,10 +229,16 @@ class Tf_ModuleProcessor { fullNamePrefix = &localPrefix; } - ret = raw_function( - _InvokeWithErrorHandling( - fn, *fullNamePrefix + "." + name, *fullNamePrefix)); - + ret = boost::python::detail::make_raw_function( + boost::python::objects::py_function( + _InvokeWithErrorHandling( + fn, *fullNamePrefix + "." + name, *fullNamePrefix), + boost::mpl::vector1(), + /*min_args =*/ 0, + /*max_args =*/ ~0 + ) + ); + ret.attr("__doc__") = fn.attr("__doc__"); } @@ -399,8 +405,12 @@ void Tf_PyInitWrapModule( const char* packageTag, const char* packageTag2) { + // Starting with Python 3.7, the GIL is initialized as part of + // Py_Initialize(). Python 3.9 deprecated explicit GIL initialization. +#if PY_VERSION_HEX < 0x03070000 // Ensure the python GIL is created. PyEval_InitThreads(); +#endif // Tell the tracing mechanism that python is alive. Tf_PyTracingPythonInitialized(); diff --git a/pxr/base/tf/pyTracing.cpp b/pxr/base/tf/pyTracing.cpp index 62262d7d75..4b201744fa 100644 --- a/pxr/base/tf/pyTracing.cpp +++ b/pxr/base/tf/pyTracing.cpp @@ -35,8 +35,9 @@ #include -// This is from python, needed for PyFrameObject. +// These are from python, needed for PyFrameObject. #include +#include #include #include @@ -105,16 +106,29 @@ static void _SetTraceFnEnabled(bool enable) { } +#if PY_VERSION_HEX < 0x030900B1 +// Define PyFrame_GetCode() on Python 3.8 and older: +// https://docs.python.org/3.11/whatsnew/3.11.html#id6 +static inline PyCodeObject* PyFrame_GetCode(PyFrameObject *frame) +{ + Py_INCREF(frame->f_code); + return frame->f_code; +} +#endif + + static int _TracePythonFn(PyObject *, PyFrameObject *frame, int what, PyObject *arg) { // Build up a trace info struct. TfPyTraceInfo info; + PyCodeObject * code = PyFrame_GetCode(frame); info.arg = arg; - info.funcName = TfPyString_AsString(frame->f_code->co_name); - info.fileName = TfPyString_AsString(frame->f_code->co_filename); - info.funcLine = frame->f_code->co_firstlineno; + info.funcName = TfPyString_AsString(code->co_name); + info.fileName = TfPyString_AsString(code->co_filename); + info.funcLine = code->co_firstlineno; info.what = what; + Py_DECREF(code); _InvokeTraceFns(info); diff --git a/pxr/base/tf/pyUtils.cpp b/pxr/base/tf/pyUtils.cpp index f3550f2287..d12d1b5619 100644 --- a/pxr/base/tf/pyUtils.cpp +++ b/pxr/base/tf/pyUtils.cpp @@ -40,6 +40,7 @@ #include #include +#include #include #include diff --git a/pxr/base/tf/pyWrapContext.cpp b/pxr/base/tf/pyWrapContext.cpp index befe042ed7..4523e03eb9 100644 --- a/pxr/base/tf/pyWrapContext.cpp +++ b/pxr/base/tf/pyWrapContext.cpp @@ -25,7 +25,7 @@ #include "pxr/pxr.h" #include "pxr/base/tf/pyWrapContext.h" - +#include "pxr/base/tf/diagnosticLite.h" #include "pxr/base/tf/instantiateSingleton.h" PXR_NAMESPACE_OPEN_SCOPE diff --git a/pxr/base/tf/refPtr.cpp b/pxr/base/tf/refPtr.cpp index b09687ad67..8949b6c532 100644 --- a/pxr/base/tf/refPtr.cpp +++ b/pxr/base/tf/refPtr.cpp @@ -35,44 +35,105 @@ PXR_NAMESPACE_OPEN_SCOPE int Tf_RefPtr_UniqueChangedCounter::_AddRef(TfRefBase const *refBase) { + // Read the current value, and try to CAS it one greater if it looks like we + // won't take it 1 -> 2. If we're successful at this, we can skip the whole + // locking business. If instead the current count is 1, we pay the price + // with the locking stuff. + auto &counter = refBase->GetRefCount()._counter; + + int prevCount = counter.load(); + while (prevCount != 1) { + if (counter.compare_exchange_weak(prevCount, prevCount+1)) { + return prevCount; + } + } + + // prevCount is 1 or it changed in the meantime. TfRefBase::UniqueChangedListener const &listener = TfRefBase::_uniqueChangedListener; listener.lock(); - int oldValue = refBase->GetRefCount()._FetchAndAdd(1); - if (oldValue == 1) + prevCount = refBase->GetRefCount()._FetchAndAdd(1); + if (prevCount == 1) { listener.func(refBase, false); + } listener.unlock(); - return oldValue; + + return prevCount; } bool Tf_RefPtr_UniqueChangedCounter::_RemoveRef(TfRefBase const *refBase) { + // Read the current value, and try to CAS it one less if it looks like we + // won't take it 2 -> 1. If we're successful at this, we can skip the whole + // locking business. If instead the current count is 2, we pay the price + // with the locking stuff. + auto &counter = refBase->GetRefCount()._counter; + + int prevCount = counter.load(); + while (prevCount != 2) { + if (counter.compare_exchange_weak(prevCount, prevCount-1)) { + return prevCount == 1; + } + } + + // prevCount is 2 or it changed in the meantime. TfRefBase::UniqueChangedListener const &listener = TfRefBase::_uniqueChangedListener; listener.lock(); - int oldValue = refBase->GetRefCount()._FetchAndAdd(-1); - if (oldValue == 2) + prevCount = refBase->GetRefCount()._FetchAndAdd(-1); + if (prevCount == 2) { listener.func(refBase, true); - + } listener.unlock(); - return oldValue == 1; + + return prevCount == 1; } bool Tf_RefPtr_UniqueChangedCounter::_AddRefIfNonzero( TfRefBase const *refBase) { + // Read the current value, and try to CAS it one greater if it looks like we + // won't take it 1 -> 2. If we're successful at this, we can skip the whole + // locking business. If instead the current count is 1, we pay the price + // with the locking stuff. + auto &counter = refBase->GetRefCount()._counter; + + int prevCount = counter.load(); + while (prevCount != 0 && prevCount != 1) { + if (counter.compare_exchange_weak(prevCount, prevCount+1)) { + return true; + } + } + + // If we saw a 0, return false. + if (prevCount == 0) { + return false; + } + + // Otherwise we saw a 1, so we may have to lock & invoke the unique changed + // counter. TfRefBase::UniqueChangedListener const &listener = TfRefBase::_uniqueChangedListener; listener.lock(); - auto &counter = refBase->GetRefCount()._counter; - int oldValue = counter.load(std::memory_order_relaxed); - if (oldValue == 0) - return false; - if (oldValue == 1) { - listener.func(refBase, false); + prevCount = counter.load(); + while (true) { + if (prevCount == 0) { + listener.unlock(); + return false; + } + if (prevCount == 1) { + listener.func(refBase, false); + counter.store(2, std::memory_order_relaxed); + listener.unlock(); + return true; + } + if (counter.compare_exchange_weak(prevCount, prevCount+1)) { + listener.unlock(); + return true; + } } - counter.store(oldValue + 1, std::memory_order_relaxed); + // Unreachable... listener.unlock(); return true; } diff --git a/pxr/base/tf/testenv/atomicOfstreamWrapper.cpp b/pxr/base/tf/testenv/atomicOfstreamWrapper.cpp index 95da21072d..6207e739f2 100644 --- a/pxr/base/tf/testenv/atomicOfstreamWrapper.cpp +++ b/pxr/base/tf/testenv/atomicOfstreamWrapper.cpp @@ -27,6 +27,7 @@ #include "pxr/pxr.h" #include "pxr/base/tf/atomicOfstreamWrapper.h" +#include "pxr/base/tf/diagnosticLite.h" #include "pxr/base/tf/fileUtils.h" #include "pxr/base/tf/ostreamMethods.h" #include "pxr/base/tf/pathUtils.h" diff --git a/pxr/base/tf/testenv/bitUtils.cpp b/pxr/base/tf/testenv/bitUtils.cpp index b21e8bf8c9..5381ea8d3c 100644 --- a/pxr/base/tf/testenv/bitUtils.cpp +++ b/pxr/base/tf/testenv/bitUtils.cpp @@ -25,6 +25,7 @@ #include "pxr/pxr.h" #include "pxr/base/tf/regTest.h" #include "pxr/base/tf/bitUtils.h" +#include "pxr/base/tf/diagnosticLite.h" #include "pxr/base/arch/defines.h" diff --git a/pxr/base/tf/testenv/denseHashMap.cpp b/pxr/base/tf/testenv/denseHashMap.cpp index 4a680d8411..a9576d2cca 100644 --- a/pxr/base/tf/testenv/denseHashMap.cpp +++ b/pxr/base/tf/testenv/denseHashMap.cpp @@ -24,6 +24,7 @@ #include "pxr/pxr.h" #include "pxr/base/tf/denseHashMap.h" +#include "pxr/base/tf/diagnosticLite.h" #include "pxr/base/tf/hash.h" #include "pxr/base/tf/regTest.h" #include "pxr/base/tf/stringUtils.h" diff --git a/pxr/base/tf/testenv/dl.cpp b/pxr/base/tf/testenv/dl.cpp index d98b87cba8..ec9f9c1549 100644 --- a/pxr/base/tf/testenv/dl.cpp +++ b/pxr/base/tf/testenv/dl.cpp @@ -26,6 +26,7 @@ #include "pxr/base/tf/regTest.h" #include "pxr/base/tf/debugCodes.h" #include "pxr/base/tf/debug.h" +#include "pxr/base/tf/diagnosticLite.h" #include "pxr/base/tf/stringUtils.h" #include "pxr/base/tf/dl.h" #include "pxr/base/arch/fileSystem.h" diff --git a/pxr/base/tf/testenv/envSetting.cpp b/pxr/base/tf/testenv/envSetting.cpp index 753b6b8603..805759c409 100644 --- a/pxr/base/tf/testenv/envSetting.cpp +++ b/pxr/base/tf/testenv/envSetting.cpp @@ -22,6 +22,7 @@ // language governing permissions and limitations under the Apache License. // #include "pxr/pxr.h" +#include "pxr/base/tf/diagnosticLite.h" #include "pxr/base/tf/envSetting.h" #include "pxr/base/tf/regTest.h" #include "pxr/base/arch/attributes.h" diff --git a/pxr/base/tf/testenv/functionRef.cpp b/pxr/base/tf/testenv/functionRef.cpp index 8d7d1e6b52..aee75461f3 100644 --- a/pxr/base/tf/testenv/functionRef.cpp +++ b/pxr/base/tf/testenv/functionRef.cpp @@ -24,6 +24,7 @@ #include "pxr/pxr.h" #include "pxr/base/tf/functionRef.h" +#include "pxr/base/tf/diagnosticLite.h" #include "pxr/base/tf/regTest.h" PXR_NAMESPACE_USING_DIRECTIVE @@ -70,6 +71,38 @@ Test_TfFunctionRef() TF_AXIOM(f3(1) == f2(1)); + // Copy constructed objects should refer to the original function rather + // than forming a reference to a reference. + { + const auto ok = [] { }; + const auto error = [] { + TF_FATAL_ERROR("Constructed new reference to callable instead of " + "copying"); + }; + TfFunctionRef ref(ok); + TfFunctionRef refCopy(ref); + ref = error; + refCopy(); + } + + // Copy assigned objects should refer to the original function rather + // than forming a reference to a reference. + { + const auto ok = [] { }; + const auto error1 = [] { + TF_FATAL_ERROR("Failed to assign reference"); + }; + const auto error2 = [] { + TF_FATAL_ERROR("Assigned new reference to callable instead of " + "copying"); + }; + TfFunctionRef ref(ok); + TfFunctionRef refCopy(error1); + refCopy = ref; + ref = error2; + refCopy(); + } + return true; } diff --git a/pxr/base/tf/testenv/mallocTag.cpp b/pxr/base/tf/testenv/mallocTag.cpp index 9befe5b05e..85e8e96f0e 100644 --- a/pxr/base/tf/testenv/mallocTag.cpp +++ b/pxr/base/tf/testenv/mallocTag.cpp @@ -22,6 +22,7 @@ // language governing permissions and limitations under the Apache License. // #include "pxr/pxr.h" +#include "pxr/base/tf/diagnosticLite.h" #include "pxr/base/tf/regTest.h" #include "pxr/base/tf/mallocTag.h" #include "pxr/base/arch/defines.h" diff --git a/pxr/base/tf/testenv/pathUtils.cpp b/pxr/base/tf/testenv/pathUtils.cpp index f09db58ccb..e8d5f43fcb 100644 --- a/pxr/base/tf/testenv/pathUtils.cpp +++ b/pxr/base/tf/testenv/pathUtils.cpp @@ -22,6 +22,7 @@ // language governing permissions and limitations under the Apache License. // #include "pxr/pxr.h" +#include "pxr/base/tf/diagnosticLite.h" #include "pxr/base/tf/regTest.h" #include "pxr/base/tf/pathUtils.h" #include "pxr/base/tf/fileUtils.h" diff --git a/pxr/base/tf/testenv/pointerAndBits.cpp b/pxr/base/tf/testenv/pointerAndBits.cpp index 31f396415f..e097d83d5e 100644 --- a/pxr/base/tf/testenv/pointerAndBits.cpp +++ b/pxr/base/tf/testenv/pointerAndBits.cpp @@ -22,6 +22,7 @@ // language governing permissions and limitations under the Apache License. // #include "pxr/pxr.h" +#include "pxr/base/tf/diagnosticLite.h" #include "pxr/base/tf/regTest.h" #include "pxr/base/tf/pointerAndBits.h" diff --git a/pxr/base/tf/testenv/preprocessorUtils.cpp b/pxr/base/tf/testenv/preprocessorUtils.cpp index 588f0c15fa..6ed9e2e6a9 100644 --- a/pxr/base/tf/testenv/preprocessorUtils.cpp +++ b/pxr/base/tf/testenv/preprocessorUtils.cpp @@ -24,6 +24,7 @@ #define TF_MAX_ARITY 24 #include "pxr/pxr.h" +#include "pxr/base/tf/diagnosticLite.h" #include "pxr/base/tf/regTest.h" #include "pxr/base/tf/preprocessorUtils.h" #include "pxr/base/tf/preprocessorUtilsLite.h" diff --git a/pxr/base/tf/testenv/registryManagerUnload.cpp b/pxr/base/tf/testenv/registryManagerUnload.cpp index 4e59837974..5b0b198be2 100644 --- a/pxr/base/tf/testenv/registryManagerUnload.cpp +++ b/pxr/base/tf/testenv/registryManagerUnload.cpp @@ -26,6 +26,7 @@ #include "pxr/base/tf/regTest.h" #include "pxr/base/tf/debugCodes.h" #include "pxr/base/tf/debug.h" +#include "pxr/base/tf/diagnosticLite.h" #include "pxr/base/tf/dl.h" #include "pxr/base/tf/registryManager.h" #include "pxr/base/tf/stringUtils.h" diff --git a/pxr/base/tf/testenv/scopeDescription.cpp b/pxr/base/tf/testenv/scopeDescription.cpp index 24f3be7d08..2103047938 100644 --- a/pxr/base/tf/testenv/scopeDescription.cpp +++ b/pxr/base/tf/testenv/scopeDescription.cpp @@ -23,6 +23,7 @@ // #include "pxr/pxr.h" +#include "pxr/base/tf/diagnosticLite.h" #include "pxr/base/tf/regTest.h" #include "pxr/base/tf/scopeDescription.h" #include "pxr/base/tf/stopwatch.h" diff --git a/pxr/base/tf/testenv/stringUtils.cpp b/pxr/base/tf/testenv/stringUtils.cpp index 41eddf9039..e404a687c4 100644 --- a/pxr/base/tf/testenv/stringUtils.cpp +++ b/pxr/base/tf/testenv/stringUtils.cpp @@ -22,6 +22,7 @@ // language governing permissions and limitations under the Apache License. // #include "pxr/pxr.h" +#include "pxr/base/tf/diagnosticLite.h" #include "pxr/base/tf/regTest.h" #include "pxr/base/tf/stringUtils.h" #include "pxr/base/arch/defines.h" diff --git a/pxr/base/tf/token.cpp b/pxr/base/tf/token.cpp index f44697a7b4..e61c882efe 100644 --- a/pxr/base/tf/token.cpp +++ b/pxr/base/tf/token.cpp @@ -88,11 +88,13 @@ struct Tf_TokenRegistry static const size_t _NumSets = 128; static const size_t _SetMask = _NumSets-1; - // Utility to pad an instance to take up a cache line to avoid false - // sharing. + // Utility to pad an instance to take up a whole number of cache lines to + // avoid false sharing. template - struct alignas(ARCH_CACHE_LINE_SIZE) _CacheLinePadded { + struct _CacheLinePadded { T val; + char _unused_padding[ + ARCH_CACHE_LINE_SIZE-(sizeof(T) % ARCH_CACHE_LINE_SIZE)]; }; // It's not ideal to use TfHashSet here. It would be better to use diff --git a/pxr/base/tf/type.cpp b/pxr/base/tf/type.cpp index 63b996995c..78c87ab04a 100644 --- a/pxr/base/tf/type.cpp +++ b/pxr/base/tf/type.cpp @@ -538,6 +538,11 @@ TfType::_FindByTypeid(const std::type_info &typeInfo) if (ARCH_LIKELY(info)) { return info->canonicalTfType; } + + // Must release the registry lock, since FindByName calls FindDerivedByName, + // and it will attempt to take the lock itself. + readLock.release(); + // It's possible that this type is only declared and not yet defined. In // that case we will fail to find it by type_info, so attempt to find the // type by name instead. @@ -937,8 +942,8 @@ TfType::DefinePythonClass(const TfPyObjWrapper & classObj) const return; } auto &r = Tf_TypeRegistry::GetInstance(); - ScopedLock infoLock(_info->mutex, /*write=*/true); ScopedLock regLock(r.GetMutex(), /*write=*/true); + ScopedLock infoLock(_info->mutex, /*write=*/true); if (!TfPyIsNone(_info->pyClass)) { infoLock.release(); regLock.release(); @@ -955,8 +960,8 @@ TfType::_DefineCppType(const std::type_info & typeInfo, size_t sizeofType, bool isPodType, bool isEnumType) const { auto &r = Tf_TypeRegistry::GetInstance(); - ScopedLock infoLock(_info->mutex, /*write=*/true); ScopedLock regLock(r.GetMutex(), /*write=*/true); + ScopedLock infoLock(_info->mutex, /*write=*/true); if (_info->typeInfo.load() != nullptr) { infoLock.release(); regLock.release(); @@ -1200,8 +1205,8 @@ TfType::AddAlias(TfType base, const string & name) const std::string errMsg; { auto &r = Tf_TypeRegistry::GetInstance(); - ScopedLock infoLock(base._info->mutex, /*write=*/true); ScopedLock regLock(r.GetMutex(), /*write=*/true); + ScopedLock infoLock(base._info->mutex, /*write=*/true); // We do not need to hold our own lock here. r.AddTypeAlias(base._info, this->_info, name, &errMsg); } diff --git a/pxr/base/vt/CMakeLists.txt b/pxr/base/vt/CMakeLists.txt index 0919bd1d51..6d103ebcbb 100644 --- a/pxr/base/vt/CMakeLists.txt +++ b/pxr/base/vt/CMakeLists.txt @@ -31,14 +31,12 @@ pxr_library(vt PUBLIC_HEADERS api.h traits.h - operators.h + typeHeaders.h + visitValue.h PYTHON_PUBLIC_HEADERS pyOperators.h - PRIVATE_HEADERS - typeHeaders.h - PYTHON_CPPFILES moduleDeps.cpp diff --git a/pxr/base/vt/array.cpp b/pxr/base/vt/array.cpp index 8c35d85826..811a946e24 100644 --- a/pxr/base/vt/array.cpp +++ b/pxr/base/vt/array.cpp @@ -24,6 +24,7 @@ #include "pxr/pxr.h" #include "pxr/base/vt/array.h" +#include "pxr/base/vt/typeHeaders.h" #include "pxr/base/tf/envSetting.h" #include "pxr/base/tf/stackTrace.h" #include "pxr/base/tf/stringUtils.h" @@ -44,4 +45,10 @@ Vt_ArrayBase::_DetachCopyHook(char const *funcName) const } } +// Instantiate basic array templates. +#define VT_ARRAY_EXPLICIT_INST(r, unused, elem) \ + template class VtArray< VT_TYPE(elem) >; +BOOST_PP_SEQ_FOR_EACH(VT_ARRAY_EXPLICIT_INST, ~, VT_SCALAR_VALUE_TYPES) + + PXR_NAMESPACE_CLOSE_SCOPE diff --git a/pxr/base/vt/array.h b/pxr/base/vt/array.h index 2d31371027..30ecfdbc8b 100644 --- a/pxr/base/vt/array.h +++ b/pxr/base/vt/array.h @@ -29,7 +29,6 @@ #include "pxr/pxr.h" #include "pxr/base/vt/api.h" #include "pxr/base/vt/hash.h" -#include "pxr/base/vt/operators.h" #include "pxr/base/vt/streamOut.h" #include "pxr/base/vt/traits.h" #include "pxr/base/vt/types.h" @@ -233,45 +232,27 @@ class VtArray : public Vt_ArrayBase { typedef ELEM ElementType; typedef ELEM value_type; - template - class PointerIterator - : public boost::iterator_adaptor, Value *> { - public: - PointerIterator() : - PointerIterator::iterator_adaptor_(0) {} - explicit PointerIterator(Value *p) : - PointerIterator::iterator_adaptor_(p) {} - template - PointerIterator(PointerIterator const &other, - typename boost::enable_if_convertible - ::type* = 0) : - PointerIterator::iterator_adaptor_(other.base()) {} - private: - friend class boost::iterator_core_access; - }; - /// \defgroup STL_API STL-like API /// @{ /// Iterator type. - typedef PointerIterator iterator; + using iterator = ElementType *; /// Const iterator type. - typedef PointerIterator const_iterator; + using const_iterator = ElementType const *; + /// Reverse iterator type. typedef boost::reverse_iterator reverse_iterator; /// Reverse const iterator type. typedef boost::reverse_iterator const_reverse_iterator; /// Reference type. - typedef typename PointerIterator::reference - reference; + typedef ElementType &reference; /// Const reference type. - typedef typename PointerIterator::reference - const_reference; + typedef ElementType const &const_reference; /// Pointer type. - typedef typename PointerIterator::pointer pointer; + typedef ElementType *pointer; /// Const pointer type. - typedef typename PointerIterator::pointer const_pointer; + typedef ElementType const *const_pointer; /// @} @@ -807,18 +788,6 @@ class VtArray : public Vt_ArrayBase { return !(*this == other); } -ARCH_PRAGMA_PUSH -ARCH_PRAGMA_FORCING_TO_BOOL -ARCH_PRAGMA_UNSAFE_USE_OF_BOOL -ARCH_PRAGMA_UNARY_MINUS_ON_UNSIGNED - VTOPERATOR_CPPARRAY(+) - VTOPERATOR_CPPARRAY(-) - VTOPERATOR_CPPARRAY(*) - VTOPERATOR_CPPARRAY(/) - VTOPERATOR_CPPARRAY(%) - VTOPERATOR_CPPARRAY_UNARY(-) -ARCH_PRAGMA_POP - public: // XXX -- Public so VtValue::_ArrayHelper::GetShapeData() has access. Vt_ShapeData const *_GetShapeData() const { @@ -929,6 +898,12 @@ ARCH_PRAGMA_POP value_type *_data; }; +// Declare basic array instantiations as extern templates. They are explicitly +// instantiated in array.cpp. +#define VT_ARRAY_EXTERN_TMPL(r, unused, elem) \ + extern template class VtArray< VT_TYPE(elem) >; +BOOST_PP_SEQ_FOR_EACH(VT_ARRAY_EXTERN_TMPL, ~, VT_SCALAR_VALUE_TYPES) + template typename std::enable_if(), size_t>::type hash_value(VtArray const &array) { @@ -943,6 +918,112 @@ hash_value(VtArray const &array) { template struct VtIsArray< VtArray > : public std::true_type {}; + +#define VTOPERATOR_CPPARRAY(op) \ + template \ + VtArray \ + operator op (VtArray const &lhs, VtArray const &rhs) \ + { \ + /* accept empty vecs */ \ + if (!lhs.empty() && !rhs.empty() && lhs.size() != rhs.size()) { \ + TF_CODING_ERROR("Non-conforming inputs for operator %s", #op); \ + return VtArray(); \ + } \ + /* promote empty vecs to vecs of zeros */ \ + const bool leftEmpty = lhs.size() == 0, rightEmpty = rhs.size() == 0; \ + VtArray ret(leftEmpty ? rhs.size() : lhs.size()); \ + T zero = VtZero(); \ + if (leftEmpty) { \ + std::transform(rhs.begin(), rhs.end(), ret.begin(), \ + [zero](T const &r) { return T(zero op r); }); \ + } \ + else if (rightEmpty) { \ + std::transform(lhs.begin(), lhs.end(), ret.begin(), \ + [zero](T const &l) { return T(l op zero); }); \ + } \ + else { \ + std::transform(lhs.begin(), lhs.end(), rhs.begin(), ret.begin(), \ + [](T const &l, T const &r) { return T(l op r); }); \ + } \ + return ret; \ + } + +ARCH_PRAGMA_PUSH +ARCH_PRAGMA_FORCING_TO_BOOL +ARCH_PRAGMA_UNSAFE_USE_OF_BOOL +ARCH_PRAGMA_UNARY_MINUS_ON_UNSIGNED + +VTOPERATOR_CPPARRAY(+); +VTOPERATOR_CPPARRAY(-); +VTOPERATOR_CPPARRAY(*); +VTOPERATOR_CPPARRAY(/); +VTOPERATOR_CPPARRAY(%); + +template +VtArray +operator-(VtArray const &a) { + VtArray ret(a.size()); + std::transform(a.begin(), a.end(), ret.begin(), + [](T const &x) { return -x; }); + return ret; +} + +ARCH_PRAGMA_POP + +// Operations on scalars and arrays +// These are free functions defined in Array.h +#define VTOPERATOR_CPPSCALAR_TYPE(op,arraytype,scalartype,rettype) \ + template \ + VtArray \ + operator op (scalartype const &scalar, \ + VtArray const &vec) { \ + VtArray ret(vec.size()); \ + for (size_t i = 0; i \ + VtArray \ + operator op (VtArray const &vec, \ + scalartype const &scalar) { \ + VtArray ret(vec.size()); \ + for (size_t i = 0; i \ + typename boost::disable_if, \ + VtArray >::type \ + operator op (double const &scalar, \ + VtArray const &vec) { \ + VtArray ret(vec.size()); \ + for (size_t i = 0; i \ + typename boost::disable_if, \ + VtArray >::type \ + operator op (VtArray const &vec, \ + double const &scalar) { \ + VtArray ret(vec.size()); \ + for (size_t i = 0; i -#include - -// -// Operators.h -// -// Define macros to make it easier to create and wrap operators that work -// with multiple types. In general, we want most operators to allow -// array OP array -// array OP scalar -// scalar OP array -// array OP tuple -// tuple OP array -// array OP list -// list OP array -// -// where the operators work element-by-element. For arrays, this means they -// need to be the same size and shape. For tuples and lists, this means -// the array shape is ignored, and the array and tuple/list need to contain the -// same number of elements. For scalars, this means the same scalar value is -// used for every array element. Naturally we require that the types held in -// the array, tuple, list, and scalar are all the same (i.e no multiplying -// a VtDoubleArray by a GfVec3d). -// -// However, we do have special cases for -// array OP double -// double OP array -// for some OPs, for cases where you may want to do element-by-element -// operations with a double when the array type is *not* double (e.g. Vec3d). -// This is done in Core/Anim, for example, for spline calculations on the -// underlying data type. -// -// We define macros because this is so repetitive, and because they need to be -// defined in different places (the array operators on the class, the array and -// scalar operators as free functions, and the tuple/list operators in the -// Python wrapping of course). Having them here allows us to put them all -// in one place for better editability. - -// ------------------------------------------------------------------------- -// C++ operator definitions -// ------------------------------------------------------------------------- -// These will be callable from C++, and define operations between arrays -// and between arrays and scalars. - -PXR_NAMESPACE_OPEN_SCOPE - -// Operations on arrays -// These go into the class definition for VtArray -#define VTOPERATOR_CPPARRAY(op) \ - VtArray operator op (VtArray const &other) const { \ - /* accept empty vecs */ \ - if ((size()!=0 && other.size()!=0) && \ - (size() != other.size())) { \ - TF_CODING_ERROR("Non-conforming inputs for operator %s",#op); \ - return VtArray(); \ - } \ - /* promote empty vecs to vecs of zeros */ \ - const bool thisEmpty = size() == 0, \ - otherEmpty = other.size() == 0; \ - VtArray ret(thisEmpty ? other.size() : size()); \ - ElementType zero = VtZero(); \ - for (size_t i = 0, n = ret.size(); i != n; ++i) { \ - ret[i] = (thisEmpty ? zero : (*this)[i]) op \ - (otherEmpty ? zero : other[i]); \ - } \ - return ret; \ - } - -#define VTOPERATOR_CPPARRAY_UNARY(op) \ - VtArray operator op () const { \ - VtArray ret(size()); \ - for (size_t i = 0, sz = ret.size(); i != sz; ++i) { \ - ret[i] = op (*this)[i]; \ - } \ - return ret; \ - } - -// Operations on scalars and arrays -// These are free functions defined in Array.h -#define VTOPERATOR_CPPSCALAR_TYPE(op,arraytype,scalartype,rettype) \ - template \ - VtArray \ - operator op (scalartype const &scalar, \ - VtArray const &vec) { \ - VtArray ret(vec.size()); \ - for (size_t i = 0; i \ - VtArray \ - operator op (VtArray const &vec, \ - scalartype const &scalar) { \ - VtArray ret(vec.size()); \ - for (size_t i = 0; i \ - typename boost::disable_if, \ - VtArray >::type \ - operator op (double const &scalar, \ - VtArray const &vec) { \ - VtArray ret(vec.size()); \ - for (size_t i = 0; i \ - typename boost::disable_if, \ - VtArray >::type \ - operator op (VtArray const &vec, \ - double const &scalar) { \ - VtArray ret(vec.size()); \ - for (size_t i = 0; i #include +#include #include #include #include #include +#include #include using std::string; @@ -88,7 +92,6 @@ static void die(const std::string &msg) { TF_FATAL_ERROR("ERROR: %s failed.", msg.c_str()); } - static void testArray() { VtDoubleArray da(60); @@ -1684,6 +1687,85 @@ testCombinedVtValueProxies() TF_AXIOM(eproxy.IsHolding<_TypedProxy>()); } +struct Stringify +{ + std::string operator()(int x) const { + return TfStringPrintf("int: %d", x); + }; + + std::string operator()(double x) const { + return TfStringPrintf("double: %.2f", x); + } + + std::string operator()(std::string const &str) const { + return TfStringPrintf("string: '%s'", str.c_str()); + }; + + template + std::string operator()(VtArray const &arr) const { + return TfStringPrintf("array: sz=%zu", arr.size()); + } + + std::string operator()(VtValue const &unknown) const { + return "unknown type"; + } +}; + +struct RoundOrMinusOne +{ + int operator()(int x) const { return x; } + + int operator()(double x) const { return static_cast(rint(x)); } + + int operator()(VtValue const &val) const { return -1; } +}; + +struct GetArraySize +{ + template + size_t operator()(VtArray const &array) const { + return array.size(); + } + + size_t operator()(VtValue const &val) const { + return ~0; + } +}; + +static void +testVisitValue() +{ + VtValue iv(123); + VtValue dv(1.23); + VtValue fv(2.34f); + VtValue hv(GfHalf(3.45)); + VtValue sv(std::string("hello")); + VtValue av(VtArray(123)); + VtValue ov(std::vector(123)); + + TF_AXIOM(VtVisitValue(iv, Stringify()) == "int: 123"); + TF_AXIOM(VtVisitValue(dv, Stringify()) == "double: 1.23"); + TF_AXIOM(VtVisitValue(fv, Stringify()) == "double: 2.34"); + TF_AXIOM(VtVisitValue(hv, Stringify()) == "double: 3.45"); + TF_AXIOM(VtVisitValue(sv, Stringify()) == "string: 'hello'"); + TF_AXIOM(VtVisitValue(av, Stringify()) == "array: sz=123"); + TF_AXIOM(VtVisitValue(ov, Stringify()) == "unknown type"); + + TF_AXIOM(VtVisitValue(iv, RoundOrMinusOne()) == 123); + TF_AXIOM(VtVisitValue(dv, RoundOrMinusOne()) == 1); + TF_AXIOM(VtVisitValue(fv, RoundOrMinusOne()) == 2); + TF_AXIOM(VtVisitValue(hv, RoundOrMinusOne()) == 3); + TF_AXIOM(VtVisitValue(sv, RoundOrMinusOne()) == -1); + TF_AXIOM(VtVisitValue(av, RoundOrMinusOne()) == -1); + TF_AXIOM(VtVisitValue(ov, RoundOrMinusOne()) == -1); + + TF_AXIOM(VtVisitValue(av, GetArraySize()) == 123); + TF_AXIOM(VtVisitValue(iv, GetArraySize()) == size_t(~0)); + TF_AXIOM(VtVisitValue( + VtValue(VtArray(234)), GetArraySize()) == 234); + +} + int main(int argc, char *argv[]) { testArray(); @@ -1701,6 +1783,8 @@ int main(int argc, char *argv[]) testErasedVtValueProxy(); testCombinedVtValueProxies(); + testVisitValue(); + printf("Test SUCCEEDED\n"); return 0; diff --git a/pxr/base/vt/types.h b/pxr/base/vt/types.h index 01e7291402..a5a9923b06 100644 --- a/pxr/base/vt/types.h +++ b/pxr/base/vt/types.h @@ -37,6 +37,7 @@ #include #include +#include #include #include @@ -185,7 +186,49 @@ BOOST_PP_SEQ_FOR_EACH(VT_ARRAY_TYPE_TUPLE, ~, VT_SCALAR_VALUE_TYPES) #define VT_CLASS_VALUE_TYPES \ VT_ARRAY_VALUE_TYPES VT_SCALAR_CLASS_VALUE_TYPES VT_NONARRAY_VALUE_TYPES - + +#define VT_VALUE_TYPES \ + VT_BUILTIN_VALUE_TYPES VT_CLASS_VALUE_TYPES + +// Provide compile-time value type indexes for types that are "known" to Vt -- +// specifically, those types that appear in VT_VALUE_TYPES. Note that VtArray +// and VtValue can work with other types that are not these "known" types. +// +// Base case -- unknown types get index -1. +template +constexpr int +VtGetKnownValueTypeIndex() { + return -1; +} + +// Set indexes for known types. +#define VT_SET_VALUE_TYPE_INDEX(r, unused, i, elem) \ + template <> constexpr int \ + VtGetKnownValueTypeIndex< VT_TYPE(elem) >() { \ + return i; \ + } +BOOST_PP_SEQ_FOR_EACH_I(VT_SET_VALUE_TYPE_INDEX, ~, VT_VALUE_TYPES) +#undef VT_SET_VALUE_TYPE_INDEX + +// Total number of 'known' value types. +constexpr int +VtGetNumKnownValueTypes() { + return BOOST_PP_SEQ_SIZE(VT_VALUE_TYPES); +} + +// None of the VT_VALUE_TYPES are value proxies. We want to specialize these +// templates here, since otherwise the VtIsTypedValueProxy will require a +// complete type to check if it derives VtTypedValueProxyBase. +#define VT_SPECIALIZE_IS_VALUE_PROXY(r, unused, elem) \ + template <> struct \ + VtIsValueProxy< VT_TYPE(elem) > : std::false_type {}; \ + template <> struct \ + VtIsTypedValueProxy< VT_TYPE(elem) > : std::false_type {}; \ + template <> struct \ + VtIsErasedValueProxy< VT_TYPE(elem) > : std::false_type {}; +BOOST_PP_SEQ_FOR_EACH(VT_SPECIALIZE_IS_VALUE_PROXY, ~, VT_VALUE_TYPES) +#undef VT_SPECIALIZE_IS_VALUE_PROXY + // Free functions to represent "zero" for various base types. See // specializations in Types.cpp template diff --git a/pxr/base/vt/value.h b/pxr/base/vt/value.h index e9d03ea17c..7e3529b8ed 100644 --- a/pxr/base/vt/value.h +++ b/pxr/base/vt/value.h @@ -246,6 +246,7 @@ class VtValue protected: constexpr _TypeInfo(const std::type_info &ti, const std::type_info &elementTi, + int knownTypeIndex, bool isArray, bool isHashable, bool isProxy, @@ -271,6 +272,7 @@ class VtValue _GetProxiedAsVtValueFunc getProxiedAsVtValue) : typeInfo(ti) , elementTypeInfo(elementTi) + , knownTypeIndex(knownTypeIndex) , isProxy(isProxy) , isArray(isArray) , isHashable(isHashable) @@ -363,6 +365,7 @@ class VtValue const std::type_info &typeInfo; const std::type_info &elementTypeInfo; + int knownTypeIndex; bool isProxy; bool isArray; bool isHashable; @@ -581,6 +584,7 @@ class VtValue constexpr _TypeInfoImpl() : _TypeInfo(typeid(T), _ArrayHelper::GetElementTypeid(), + VtGetKnownValueTypeIndex(), VtIsArray::value, VtIsHashable(), IsProxy, @@ -1070,6 +1074,18 @@ class VtValue /// Return the type name of the held typeid. VT_API std::string GetTypeName() const; + /// Return VtKnownValueTypeIndex for the held type T. If this value + /// holds a proxy type, resolve the proxy and return the proxied type's + /// index. If this value is empty or holds a type that is not 'known', + /// return -1. + int GetKnownValueTypeIndex() const { + if (ARCH_UNLIKELY(_IsProxy())) { + return _info->GetProxiedAsVtValue( + _storage).GetKnownValueTypeIndex(); + } + return _info.GetLiteral() ? _info->knownTypeIndex : -1; + } + /// Returns a const reference to the held object if the held object /// is of type \a T. Invokes undefined behavior otherwise. This is the /// fastest \a Get() method to use after a successful \a IsHolding() check. diff --git a/pxr/base/vt/visitValue.h b/pxr/base/vt/visitValue.h new file mode 100644 index 0000000000..65cc2a68ae --- /dev/null +++ b/pxr/base/vt/visitValue.h @@ -0,0 +1,130 @@ +// +// Copyright 2022 Pixar +// +// Licensed under the Apache License, Version 2.0 (the "Apache License") +// with the following modification; you may not use this file except in +// compliance with the Apache License and the following modification to it: +// Section 6. Trademarks. is deleted and replaced with: +// +// 6. Trademarks. This License does not grant permission to use the trade +// names, trademarks, service marks, or product names of the Licensor +// and its affiliates, except as required to comply with Section 4(c) of +// the License and to reproduce the content of the NOTICE file. +// +// You may obtain a copy of the Apache License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the Apache License with the above modification is +// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the Apache License for the specific +// language governing permissions and limitations under the Apache License. +// +#ifndef PXR_BASE_VT_VISIT_VALUE_H +#define PXR_BASE_VT_VISIT_VALUE_H + +#include "pxr/pxr.h" + +#include "pxr/base/vt/value.h" + +PXR_NAMESPACE_OPEN_SCOPE + +namespace Vt_ValueVisitDetail { + +// These two overloads do SFINAE to detect whether the visitor can be invoked +// with the given held type T. If the visitor cannot be invoked with T, it is +// instead invoked with the VtValue itself. +template ()(std::declval()))> +auto +Visit(VtValue const &val, Visitor &&visitor, int) { + return std::forward(visitor)(val.UncheckedGet()); +} + +template +auto +Visit(VtValue const &val, Visitor &&visitor, ...) { + return std::forward(visitor)(val); +} + +} // Vt_ValueVisitDetail + +/// Invoke \p visitor with \p value's held object if \p value holds an object of +/// one of the "known" value types (those in VT_VALUE_TYPES, see vt/types.h). +/// If \p value does not hold a known type, or if it is empty, or if \p visitor +/// cannot be called with an object of the held type, then call \p visitor with +/// \p value itself. Note this means that \p visitor must be callable with a +/// VtValue argument. +/// +/// VtVisitValue() can be lower overhead compared to a chained-if of +/// VtValue::IsHolding() calls, or a hash-table-lookup dispatch. Additionally, +/// visitors can handle related types with a single case, rather than calling +/// out all types individually. For example: +/// +/// \code +/// // If the value holds an array return its size, otherwise size_t(-1). +/// struct GetArraySize { +/// template +/// size_t operator()(VtArray const &array) const { +/// return array.size(); +/// } +/// size_t operator()(VtValue const &val) const { +/// return size_t(-1); +/// } +/// }; +/// +/// VtVisitValue(VtValue(VtIntArray(123)), GetArraySize()) -> 123 +/// VtVisitValue(VtValue(VtDoubleArray(234)), GetArraySize()) -> 234 +/// VtVisitValue(VtValue(VtVec3fArray(345)), GetArraySize()) -> 345 +/// VtVisitValue(VtValue("not-a-vt-array"), GetArraySize()) -> size_t(-1) +/// \endcode +/// +/// Note that the visitor is invoked as a normal C++ call expression, so +/// implicit conversions and standard overload resolution (including controlling +/// overload resolution via techniques like enable_if) can take place. For +/// example, consider the following, where the double-specific overload is +/// invoked for VtValues holding double, float, and GfHalf. +/// +/// \code +/// struct AsDouble { +/// double operator()(double val) const { +/// return val; +/// } +/// double operator()(VtValue const &) const { +/// return std::numeric_limits::quiet_NaN(); +/// } +/// }; +/// +/// VtVisitValue(VtValue(1.23), AsDouble()) -> 1.23 +/// VtVisitValue(VtValue(float(0.5f)), AsDouble()) -> 0.5 +/// VtVisitValue(VtValue(GfHalf(1.5f)), AsDouble()) -> 1.5 +/// VtVisitValue(VtValue("not-convertible-to-double"), AsDouble()) -> NaN. +/// \endcode +template +auto VtVisitValue(VtValue const &value, Visitor &&visitor) +{ + // This generally gets the compiler to emit a jump table to dispatch + // directly to the code for each known value type. + switch (value.GetKnownValueTypeIndex()) { + +// Cases for known types. +#define VT_CASE_FOR_TYPE_INDEX(r, unused, i, elem) \ + case i: \ + return Vt_ValueVisitDetail::Visit( \ + value, std::forward(visitor), 0); \ + break; +BOOST_PP_SEQ_FOR_EACH_I(VT_CASE_FOR_TYPE_INDEX, ~, VT_VALUE_TYPES) +#undef VT_CASE_FOR_TYPE_INDEX + + default: + // Invoke visitor with value itself. + return Vt_ValueVisitDetail::Visit( + value, std::forward(visitor), 0); + break; + }; +} + +PXR_NAMESPACE_CLOSE_SCOPE + +#endif // PXR_BASE_VT_VISIT_VALUE_H diff --git a/pxr/base/vt/wrapArray.h b/pxr/base/vt/wrapArray.h index 23c9a21858..d2e4407e96 100644 --- a/pxr/base/vt/wrapArray.h +++ b/pxr/base/vt/wrapArray.h @@ -122,7 +122,7 @@ getitem_slice(VtArray const &self, slice idx) result[i] = *range.start; return object(result); } - catch (std::invalid_argument) { + catch (std::invalid_argument const &) { return object(); } } @@ -180,7 +180,7 @@ setArraySlice(VtArray &self, slice idx, object value, bool tile = false) T* data = self.data(); range = idx.get_indices(data, data + self.size()); } - catch (std::invalid_argument) { + catch (std::invalid_argument const &) { // Do nothing return; } diff --git a/pxr/imaging/CMakeLists.txt b/pxr/imaging/CMakeLists.txt index ebf2574d0a..91af8f272f 100644 --- a/pxr/imaging/CMakeLists.txt +++ b/pxr/imaging/CMakeLists.txt @@ -3,6 +3,7 @@ add_subdirectory(hf) add_subdirectory(hio) add_subdirectory(cameraUtil) add_subdirectory(pxOsd) +add_subdirectory(geomUtil) add_subdirectory(glf) add_subdirectory(hgi) add_subdirectory(hgiGL) diff --git a/pxr/imaging/geomUtil/CMakeLists.txt b/pxr/imaging/geomUtil/CMakeLists.txt new file mode 100644 index 0000000000..0c1fefab9d --- /dev/null +++ b/pxr/imaging/geomUtil/CMakeLists.txt @@ -0,0 +1,57 @@ +set(PXR_PREFIX pxr/imaging) +set(PXR_PACKAGE geomUtil) + +pxr_library(geomUtil + LIBRARIES + arch + gf + tf + vt + pxOsd + + PUBLIC_CLASSES + capsuleMeshGenerator + coneMeshGenerator + cuboidMeshGenerator + cylinderMeshGenerator + meshGeneratorBase + sphereMeshGenerator + + PYTHON_CPPFILES + moduleDeps.cpp + + PUBLIC_HEADERS + api.h + + PYMODULE_CPPFILES + module.cpp + wrapCapsuleMeshGenerator.cpp + wrapConeMeshGenerator.cpp + wrapCuboidMeshGenerator.cpp + wrapCylinderMeshGenerator.cpp + wrapSphereMeshGenerator.cpp + + PYMODULE_FILES + __init__.py +) + +pxr_build_test(testMeshGenerators + LIBRARIES + gf + tf + vt + geomUtil + pxOsd + CPPFILES + testenv/testMeshGenerators.cpp +) + +pxr_install_test_dir( + SRC testenv/testMeshGenerators + DEST testMeshGenerators +) + +pxr_register_test(testMeshGenerators + COMMAND "${CMAKE_INSTALL_PREFIX}/tests/testMeshGenerators" + DIFF_COMPARE generatedMeshes_closed.txt generatedMeshes_open.txt +) \ No newline at end of file diff --git a/pxr/imaging/geomUtil/__init__.py b/pxr/imaging/geomUtil/__init__.py new file mode 100644 index 0000000000..d601f63d3f --- /dev/null +++ b/pxr/imaging/geomUtil/__init__.py @@ -0,0 +1,39 @@ +# +# Copyright 2022 Pixar +# +# Licensed under the Apache License, Version 2.0 (the "Apache License") +# with the following modification; you may not use this file except in +# compliance with the Apache License and the following modification to it: +# Section 6. Trademarks. is deleted and replaced with: +# +# 6. Trademarks. This License does not grant permission to use the trade +# names, trademarks, service marks, or product names of the Licensor +# and its affiliates, except as required to comply with Section 4(c) of +# the License and to reproduce the content of the NOTICE file. +# +# You may obtain a copy of the Apache License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the Apache License with the above modification is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the Apache License for the specific +# language governing permissions and limitations under the Apache License. +# +""" +Utilities to help image common geometry. +""" + +from . import _geomUtil +from pxr import Tf +Tf.PrepareModule(_geomUtil, locals()) +del _geomUtil, Tf + +try: + import __DOC + __DOC.Execute(locals()) + del __DOC +except Exception: + pass + diff --git a/pxr/imaging/geomUtil/api.h b/pxr/imaging/geomUtil/api.h new file mode 100644 index 0000000000..8c5f208116 --- /dev/null +++ b/pxr/imaging/geomUtil/api.h @@ -0,0 +1,47 @@ +// +// Copyright 2022 Pixar +// +// Licensed under the Apache License, Version 2.0 (the "Apache License") +// with the following modification; you may not use this file except in +// compliance with the Apache License and the following modification to it: +// Section 6. Trademarks. is deleted and replaced with: +// +// 6. Trademarks. This License does not grant permission to use the trade +// names, trademarks, service marks, or product names of the Licensor +// and its affiliates, except as required to comply with Section 4(c) of +// the License and to reproduce the content of the NOTICE file. +// +// You may obtain a copy of the Apache License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the Apache License with the above modification is +// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the Apache License for the specific +// language governing permissions and limitations under the Apache License. +// +#ifndef PXR_IMAGING_GEOM_UTIL_API_H +#define PXR_IMAGING_GEOM_UTIL_API_H + +#include "pxr/base/arch/export.h" + +#if defined(PXR_STATIC) +# define GEOMUTIL_API +# define GEOMUTIL_API_TEMPLATE_CLASS(...) +# define GEOMUTIL_API_TEMPLATE_STRUCT(...) +# define GEOMUTIL_LOCAL +#else +# if defined(GEOMUTIL_EXPORTS) +# define GEOMUTIL_API ARCH_EXPORT +# define GEOMUTIL_API_TEMPLATE_CLASS(...) ARCH_EXPORT_TEMPLATE(class, __VA_ARGS__) +# define GEOMUTIL_API_TEMPLATE_STRUCT(...) ARCH_EXPORT_TEMPLATE(struct, __VA_ARGS__) +# else +# define GEOMUTIL_API ARCH_IMPORT +# define GEOMUTIL_API_TEMPLATE_CLASS(...) ARCH_IMPORT_TEMPLATE(class, __VA_ARGS__) +# define GEOMUTIL_API_TEMPLATE_STRUCT(...) ARCH_IMPORT_TEMPLATE(struct, __VA_ARGS__) +# endif +# define GEOMUTIL_LOCAL ARCH_HIDDEN +#endif + +#endif // PXR_IMAGING_GEOM_UTIL_API_H diff --git a/pxr/imaging/geomUtil/capsuleMeshGenerator.cpp b/pxr/imaging/geomUtil/capsuleMeshGenerator.cpp new file mode 100644 index 0000000000..7c14f4e715 --- /dev/null +++ b/pxr/imaging/geomUtil/capsuleMeshGenerator.cpp @@ -0,0 +1,170 @@ +// +// Copyright 2022 Pixar +// +// Licensed under the Apache License, Version 2.0 (the "Apache License") +// with the following modification; you may not use this file except in +// compliance with the Apache License and the following modification to it: +// Section 6. Trademarks. is deleted and replaced with: +// +// 6. Trademarks. This License does not grant permission to use the trade +// names, trademarks, service marks, or product names of the Licensor +// and its affiliates, except as required to comply with Section 4(c) of +// the License and to reproduce the content of the NOTICE file. +// +// You may obtain a copy of the Apache License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the Apache License with the above modification is +// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the Apache License for the specific +// language governing permissions and limitations under the Apache License. +// +#include "pxr/imaging/geomUtil/capsuleMeshGenerator.h" + +#include "pxr/imaging/pxOsd/meshTopology.h" +#include "pxr/imaging/pxOsd/tokens.h" + +#include "pxr/base/arch/math.h" +#include "pxr/base/vt/types.h" + +#include +#include +#include + +PXR_NAMESPACE_OPEN_SCOPE + + +// static +size_t +GeomUtilCapsuleMeshGenerator::ComputeNumPoints( + const size_t numRadial, + const size_t numCapAxial, + const bool closedSweep) +{ + if ((numRadial < minNumRadial) || (numCapAxial < minNumCapAxial)) { + return 0; + } + + const size_t numRadialPoints = + _ComputeNumRadialPoints(numRadial, closedSweep); + + return ((2 * numCapAxial) * numRadialPoints) + 2; +} + +// static +PxOsdMeshTopology +GeomUtilCapsuleMeshGenerator::GenerateTopology( + const size_t numRadial, + const size_t numCapAxial, + const bool closedSweep) +{ + if ((numRadial < minNumRadial) || (numCapAxial < minNumCapAxial)) { + return PxOsdMeshTopology(); + } + + return _GenerateCappedQuadTopology( + numRadial, + /* numQuadStrips = */ (2 * (numCapAxial - 1)) + 1, + /* bottomCapStyle = */ CapStyleSharedEdge, + /* topCapStyle = */ CapStyleSharedEdge, + closedSweep); +} + +// static +template +void +GeomUtilCapsuleMeshGenerator::_GeneratePointsImpl( + const size_t numRadial, + const size_t numCapAxial, + const typename PointType::ScalarType bottomRadius, + const typename PointType::ScalarType topRadius, + const typename PointType::ScalarType height, + const typename PointType::ScalarType bottomCapHeight, + const typename PointType::ScalarType topCapHeight, + const typename PointType::ScalarType sweepDegrees, + const _PointWriter& ptWriter) +{ + using ScalarType = typename PointType::ScalarType; + + if ((numRadial < minNumRadial) || (numCapAxial < minNumCapAxial)) { + return; + } + + const ScalarType twoPi = 2.0 * M_PI; + const ScalarType sweepRadians = + GfClamp((ScalarType) GfDegreesToRadians(sweepDegrees), -twoPi, twoPi); + const bool closedSweep = GfIsClose(std::abs(sweepRadians), twoPi, 1e-6); + + // Construct a circular arc of unit radius in the XY plane. + const size_t numRadialPoints = + _ComputeNumRadialPoints(numRadial, closedSweep); + std::vector> ringXY(numRadialPoints); + + for (size_t radIdx = 0; radIdx < numRadialPoints; ++radIdx) { + // Longitude range: [0, sweep] + const ScalarType longAngle = + (ScalarType(radIdx) / ScalarType(numRadial)) * sweepRadians; + ringXY[radIdx][0] = cos(longAngle); + ringXY[radIdx][1] = sin(longAngle); + } + + // Bottom point: + ptWriter.Write(PointType(0.0, 0.0, -(bottomCapHeight + (0.5 * height)))); + + // Bottom hemisphere latitude rings: + for (size_t axIdx = 1; axIdx < (numCapAxial + 1); ++axIdx) { + // Latitude range: (-0.5pi, 0] + const ScalarType latAngle = + ((ScalarType(axIdx) / ScalarType(numCapAxial)) - 1.0) * + (0.5 * M_PI); + + const ScalarType radScale = cos(latAngle); + const ScalarType latitude = + -(0.5 * height) + (bottomCapHeight * sin(latAngle)); + + for (size_t radIdx = 0; radIdx < numRadialPoints; ++radIdx) { + ptWriter.Write( + PointType(radScale * bottomRadius * ringXY[radIdx][0], + radScale * bottomRadius * ringXY[radIdx][1], + latitude)); + } + } + + // Top hemisphere latitude rings: + for (size_t axIdx = 0; axIdx < numCapAxial; ++axIdx) { + // Latitude range: [0, 0.5pi) + const ScalarType latAngle = + (ScalarType(axIdx) / ScalarType(numCapAxial)) * (0.5 * M_PI); + + const ScalarType radScale = cos(latAngle); + const ScalarType latitude = + (0.5 * height) + (topCapHeight * sin(latAngle)); + + for (size_t radIdx = 0; radIdx < numRadialPoints; ++radIdx) { + ptWriter.Write(PointType(radScale * topRadius * ringXY[radIdx][0], + radScale * topRadius * ringXY[radIdx][1], + latitude)); + } + } + + // Top point: + ptWriter.Write(PointType(0.0, 0.0, topCapHeight + (0.5 * height))); +} + +// Force-instantiate _GeneratePointsImpl for the supported point types. Only +// these instantiations will ever be needed due to the SFINAE machinery on the +// calling method template (the public GeneratePoints, in the header). +template GEOMUTIL_API void GeomUtilCapsuleMeshGenerator::_GeneratePointsImpl( + const size_t, const size_t, const float, const float, + const float, const float, const float, const float, + const GeomUtilCapsuleMeshGenerator::_PointWriter&); + +template GEOMUTIL_API void GeomUtilCapsuleMeshGenerator::_GeneratePointsImpl( + const size_t, const size_t, const double, const double, + const double, const double, const double, const double, + const GeomUtilCapsuleMeshGenerator::_PointWriter&); + + +PXR_NAMESPACE_CLOSE_SCOPE \ No newline at end of file diff --git a/pxr/imaging/geomUtil/capsuleMeshGenerator.h b/pxr/imaging/geomUtil/capsuleMeshGenerator.h new file mode 100644 index 0000000000..0d8ef696e3 --- /dev/null +++ b/pxr/imaging/geomUtil/capsuleMeshGenerator.h @@ -0,0 +1,154 @@ +// +// Copyright 2022 Pixar +// +// Licensed under the Apache License, Version 2.0 (the "Apache License") +// with the following modification; you may not use this file except in +// compliance with the Apache License and the following modification to it: +// Section 6. Trademarks. is deleted and replaced with: +// +// 6. Trademarks. This License does not grant permission to use the trade +// names, trademarks, service marks, or product names of the Licensor +// and its affiliates, except as required to comply with Section 4(c) of +// the License and to reproduce the content of the NOTICE file. +// +// You may obtain a copy of the Apache License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the Apache License with the above modification is +// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the Apache License for the specific +// language governing permissions and limitations under the Apache License. +// +#ifndef PXR_IMAGING_GEOM_UTIL_CAPSULE_MESH_GENERATOR_H +#define PXR_IMAGING_GEOM_UTIL_CAPSULE_MESH_GENERATOR_H + +#include "pxr/imaging/geomUtil/api.h" +#include "pxr/imaging/geomUtil/meshGeneratorBase.h" + +#include "pxr/pxr.h" + +PXR_NAMESPACE_OPEN_SCOPE + +class GfMatrix4d; +class PxOsdMeshTopology; + +/// This class provides an implementation for generating topology and point +/// positions on a capsule. The simplest form takes a radius and height and is +/// a cylinder capped by two hemispheres that is centered at the origin. The +/// generated capsule is made up of circular cross-sections in the XY plane. +/// Each cross-section has numRadial segments. Successive cross-sections for +/// each of the hemispheres are generated at numCapAxial locations along the Z +/// and -Z axes respectively. The height is aligned with the Z axis and +/// represents the height of just the cylindrical portion. +/// +/// An optional transform may be provided to GeneratePoints to orient the +/// capsule as necessary (e.g., whose height is along the Y axis) . +/// +/// An additional overload of GeneratePoints is provided to specify different +/// radii and heights for the bottom and top caps, as well as the sweep angle +/// for the capsule about the +Z axis. When the sweep is less than 360 degrees, +/// the generated geometry is not closed. +/// +/// Usage: +/// \code{.cpp} +/// +/// const size_t numRadial = 4, numCapAxial = 4; +/// const size_t numPoints = +/// GeomUtilCapsuleMeshGenerator::ComputeNumPoints(numRadial, numCapAxial); +/// const float radius = 1, height = 2; +/// +/// MyPointContainer points(numPoints); +/// +/// GeomUtilCapsuleMeshGenerator::GeneratePoints( +/// points.begin(), numRadial, numCapAxial, radius, height); +/// +/// \endcode +/// +class GeomUtilCapsuleMeshGenerator final + : public GeomUtilMeshGeneratorBase +{ +public: + static constexpr size_t minNumRadial = 3; + static constexpr size_t minNumCapAxial = 1; + + GEOMUTIL_API + static size_t ComputeNumPoints( + const size_t numRadial, + const size_t numCapAxial, + const bool closedSweep = true); + + GEOMUTIL_API + static PxOsdMeshTopology GenerateTopology( + const size_t numRadial, + const size_t numCapAxial, + const bool closedSweep = true); + + template::type> + static void GeneratePoints( + PointIterType iter, + const size_t numRadial, + const size_t numCapAxial, + const ScalarType radius, + const ScalarType height, + const GfMatrix4d* framePtr = nullptr) + { + constexpr ScalarType sweep = 360; + + GeneratePoints(iter, numRadial, numCapAxial, + /* bottomRadius = */ radius, + /* topRadius = */ radius, + height, + /* bottomCapHeight = */ radius, + /* topCapHeight = */ radius, + sweep, framePtr); + } + + template::type> + static void GeneratePoints( + PointIterType iter, + const size_t numRadial, + const size_t numCapAxial, + const ScalarType bottomRadius, + const ScalarType topRadius, + const ScalarType height, + const ScalarType bottomCapHeight, + const ScalarType topCapHeight, + const ScalarType sweepDegrees, + const GfMatrix4d* framePtr = nullptr) + { + using PointType = + typename std::iterator_traits::value_type; + + _GeneratePointsImpl(numRadial, numCapAxial, bottomRadius, topRadius, + height, bottomCapHeight, topCapHeight, sweepDegrees, + framePtr ? _PointWriter(iter, framePtr) + : _PointWriter(iter)); + } + + using GeomUtilMeshGeneratorBase::GeneratePoints; + +private: + template + static void _GeneratePointsImpl( + const size_t numRadial, + const size_t numCapAxial, + const typename PointType::ScalarType bottomRadius, + const typename PointType::ScalarType topRadius, + const typename PointType::ScalarType height, + const typename PointType::ScalarType bottomCapHeight, + const typename PointType::ScalarType topCapHeight, + const typename PointType::ScalarType sweep, + const _PointWriter& ptWriter); +}; + +PXR_NAMESPACE_CLOSE_SCOPE + +#endif // PXR_IMAGING_GEOM_UTIL_CAPSULE_MESH_GENERATOR_H diff --git a/pxr/imaging/geomUtil/coneMeshGenerator.cpp b/pxr/imaging/geomUtil/coneMeshGenerator.cpp new file mode 100644 index 0000000000..0e46f0fc4e --- /dev/null +++ b/pxr/imaging/geomUtil/coneMeshGenerator.cpp @@ -0,0 +1,146 @@ +// +// Copyright 2022 Pixar +// +// Licensed under the Apache License, Version 2.0 (the "Apache License") +// with the following modification; you may not use this file except in +// compliance with the Apache License and the following modification to it: +// Section 6. Trademarks. is deleted and replaced with: +// +// 6. Trademarks. This License does not grant permission to use the trade +// names, trademarks, service marks, or product names of the Licensor +// and its affiliates, except as required to comply with Section 4(c) of +// the License and to reproduce the content of the NOTICE file. +// +// You may obtain a copy of the Apache License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the Apache License with the above modification is +// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the Apache License for the specific +// language governing permissions and limitations under the Apache License. +// +#include "pxr/imaging/geomUtil/coneMeshGenerator.h" + +#include "pxr/imaging/pxOsd/meshTopology.h" +#include "pxr/imaging/pxOsd/tokens.h" + +#include "pxr/base/arch/math.h" +#include "pxr/base/vt/types.h" + +#include +#include +#include +#include + +PXR_NAMESPACE_OPEN_SCOPE + + +// static +size_t +GeomUtilConeMeshGenerator::ComputeNumPoints( + const size_t numRadial, + const bool closedSweep) +{ + if (numRadial < minNumRadial) { + return 0; + } + + const size_t numRadialPoints = + _ComputeNumRadialPoints(numRadial, closedSweep); + + return (3 * numRadialPoints) + 1; +} + +// static +PxOsdMeshTopology +GeomUtilConeMeshGenerator::GenerateTopology( + const size_t numRadial, + const bool closedSweep) +{ + if (numRadial < minNumRadial) { + return PxOsdMeshTopology(); + } + + return _GenerateCappedQuadTopology( + numRadial, + /* numQuadStrips = */ 1, + /* bottomCapStyle = */ CapStyleSeparateEdge, + /* topCapStyle = */ CapStyleNone, + closedSweep); +} + +// static +template +void +GeomUtilConeMeshGenerator::_GeneratePointsImpl( + const size_t numRadial, + const typename PointType::ScalarType radius, + const typename PointType::ScalarType height, + const typename PointType::ScalarType sweepDegrees, + const _PointWriter& ptWriter) +{ + using ScalarType = typename PointType::ScalarType; + + if (numRadial < minNumRadial) { + return; + } + + const ScalarType twoPi = 2.0 * M_PI; + const ScalarType sweepRadians = + GfClamp((ScalarType) GfDegreesToRadians(sweepDegrees), -twoPi, twoPi); + const bool closedSweep = GfIsClose(std::abs(sweepRadians), twoPi, 1e-6); + + // Construct a circular arc/ring of the specified radius in the XY plane. + const size_t numRadialPoints = + _ComputeNumRadialPoints(numRadial, closedSweep); + std::vector> ringXY(numRadialPoints); + + for (size_t radIdx = 0; radIdx < numRadialPoints; ++radIdx) { + // Longitude range: [0, 2pi) + const ScalarType longAngle = + (ScalarType(radIdx) / ScalarType(numRadial)) * sweepRadians; + ringXY[radIdx][0] = radius * cos(longAngle); + ringXY[radIdx][1] = radius * sin(longAngle); + } + + const ScalarType zMax = 0.5 * height; + const ScalarType zMin = -zMax; + + // Bottom point: + ptWriter.Write(PointType(0.0, 0.0, zMin)); + + // Bottom rings; two consecutive rings at the same point locations, the + // first for the bottom triangle fan and the second for the main + // cone quads (for normals reasons the bottom "edge" is not shared): + for (size_t ringIdx = 0; ringIdx < 2; ++ringIdx) { + for (size_t radIdx = 0; radIdx < numRadialPoints; ++radIdx) { + ptWriter.Write(PointType(ringXY[radIdx][0], + ringXY[radIdx][1], + zMin)); + } + } + + // Top "ring" (all points coincident); the cone consists of degenerate + // quads, so edges between left/right faces generate smooth normals but + // there's no continuity over the top "point" as would happen with a + // triangle fan. + for (size_t radIdx = 0; radIdx < numRadialPoints; ++radIdx) { + ptWriter.Write(PointType(0.0, 0.0, zMax)); + } +} + +// Force-instantiate _GeneratePointsImpl for the supported point types. Only +// these instantiations will ever be needed due to the SFINAE machinery on the +// calling method template (the public GeneratePoints, in the header). +template GEOMUTIL_API void GeomUtilConeMeshGenerator::_GeneratePointsImpl( + const size_t, const float, const float, const float, + const GeomUtilConeMeshGenerator::_PointWriter&); + +template GEOMUTIL_API void GeomUtilConeMeshGenerator::_GeneratePointsImpl( + const size_t, const double, const double, const double, + const GeomUtilConeMeshGenerator::_PointWriter&); + + +PXR_NAMESPACE_CLOSE_SCOPE \ No newline at end of file diff --git a/pxr/imaging/geomUtil/coneMeshGenerator.h b/pxr/imaging/geomUtil/coneMeshGenerator.h new file mode 100644 index 0000000000..522a1fc55e --- /dev/null +++ b/pxr/imaging/geomUtil/coneMeshGenerator.h @@ -0,0 +1,131 @@ +// +// Copyright 2022 Pixar +// +// Licensed under the Apache License, Version 2.0 (the "Apache License") +// with the following modification; you may not use this file except in +// compliance with the Apache License and the following modification to it: +// Section 6. Trademarks. is deleted and replaced with: +// +// 6. Trademarks. This License does not grant permission to use the trade +// names, trademarks, service marks, or product names of the Licensor +// and its affiliates, except as required to comply with Section 4(c) of +// the License and to reproduce the content of the NOTICE file. +// +// You may obtain a copy of the Apache License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the Apache License with the above modification is +// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the Apache License for the specific +// language governing permissions and limitations under the Apache License. +// +#ifndef PXR_IMAGING_GEOM_UTIL_CONE_MESH_GENERATOR_H +#define PXR_IMAGING_GEOM_UTIL_CONE_MESH_GENERATOR_H + +#include "pxr/imaging/geomUtil/api.h" +#include "pxr/imaging/geomUtil/meshGeneratorBase.h" + +#include "pxr/pxr.h" + +PXR_NAMESPACE_OPEN_SCOPE + +class GfMatrix4d; +class PxOsdMeshTopology; + +/// This class provides an implementation for generating topology and point +/// positions on a cone of a given radius and height. The cone is made up of +/// circular cross-sections in the XY plane and is centered at the origin. Each +/// cross-section has numRadial segments. The height is aligned with the Z +/// axis, with the base of the object at Z = -h/2 and apex at Z = h/2. +/// +/// An optional transform may be provided to GeneratePoints to orient the +/// cone as necessary (e.g., whose height is along the Y axis) . +/// +/// An additional overload of GeneratePoints is provided to specify the sweep +/// angle for the cone about the +Z axis. When the sweep is less than 360 +/// degrees, the generated geometry is not closed. +/// +/// Usage: +/// \code{.cpp} +/// +/// const size_t numRadial = 8; +/// const size_t numPoints = +/// GeomUtilConeMeshGenerator::ComputeNumPoints(numRadial); +/// const float radius = 1, height = 2; +/// +/// MyPointContainer points(numPoints); +/// +/// GeomUtilConeMeshGenerator::GeneratePoints( +/// points.begin(), numRadial, radius, height); +/// +/// \endcode +/// +class GeomUtilConeMeshGenerator final + : public GeomUtilMeshGeneratorBase +{ +public: + static constexpr size_t minNumRadial = 3; + + GEOMUTIL_API + static size_t ComputeNumPoints( + const size_t numRadial, + const bool closedSweep = true); + + GEOMUTIL_API + static PxOsdMeshTopology GenerateTopology( + const size_t numRadial, + const bool closedSweep = true); + + template::type> + static void GeneratePoints( + PointIterType iter, + const size_t numRadial, + const ScalarType radius, + const ScalarType height, + const GfMatrix4d* framePtr = nullptr) + { + constexpr ScalarType sweep = 360; + GeneratePoints(iter, numRadial, radius, height, sweep, framePtr); + } + + template::type> + static void GeneratePoints( + PointIterType iter, + const size_t numRadial, + const ScalarType radius, + const ScalarType height, + const ScalarType sweepDegrees, + const GfMatrix4d* framePtr = nullptr) + { + using PointType = + typename std::iterator_traits::value_type; + + _GeneratePointsImpl(numRadial, radius, height, sweepDegrees, + framePtr ? _PointWriter(iter, framePtr) + : _PointWriter(iter)); + } + + using GeomUtilMeshGeneratorBase::GeneratePoints; + +private: + + template + static void _GeneratePointsImpl( + const size_t numRadial, + const typename PointType::ScalarType radius, + const typename PointType::ScalarType height, + const typename PointType::ScalarType sweepDegrees, + const _PointWriter& ptWriter); +}; + +PXR_NAMESPACE_CLOSE_SCOPE + +#endif // PXR_IMAGING_GEOM_UTIL_CONE_MESH_GENERATOR_H diff --git a/pxr/imaging/geomUtil/cuboidMeshGenerator.cpp b/pxr/imaging/geomUtil/cuboidMeshGenerator.cpp new file mode 100644 index 0000000000..19b16b8a58 --- /dev/null +++ b/pxr/imaging/geomUtil/cuboidMeshGenerator.cpp @@ -0,0 +1,96 @@ +// +// Copyright 2022 Pixar +// +// Licensed under the Apache License, Version 2.0 (the "Apache License") +// with the following modification; you may not use this file except in +// compliance with the Apache License and the following modification to it: +// Section 6. Trademarks. is deleted and replaced with: +// +// 6. Trademarks. This License does not grant permission to use the trade +// names, trademarks, service marks, or product names of the Licensor +// and its affiliates, except as required to comply with Section 4(c) of +// the License and to reproduce the content of the NOTICE file. +// +// You may obtain a copy of the Apache License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the Apache License with the above modification is +// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the Apache License for the specific +// language governing permissions and limitations under the Apache License. +// +#include "pxr/imaging/geomUtil/cuboidMeshGenerator.h" + +#include "pxr/imaging/pxOsd/meshTopology.h" +#include "pxr/imaging/pxOsd/tokens.h" + +#include "pxr/base/vt/types.h" + +PXR_NAMESPACE_OPEN_SCOPE + + +// static +size_t +GeomUtilCuboidMeshGenerator::ComputeNumPoints() +{ + return 8; +} + +// static +PxOsdMeshTopology +GeomUtilCuboidMeshGenerator::GenerateTopology() +{ + // These never vary, we might as well share a single copy via VtArray. + static const VtIntArray countsArray{ 4, 4, 4, 4, 4, 4 }; + static const VtIntArray indicesArray{ 0, 1, 2, 3, + 4, 5, 6, 7, + 0, 6, 5, 1, + 4, 7, 3, 2, + 0, 3, 7, 6, + 4, 2, 1, 5 }; + return PxOsdMeshTopology( + PxOsdOpenSubdivTokens->bilinear, + PxOsdOpenSubdivTokens->rightHanded, + countsArray, indicesArray); +} + +// static +template +void +GeomUtilCuboidMeshGenerator::_GeneratePointsImpl( + const typename PointType::ScalarType xLength, + const typename PointType::ScalarType yLength, + const typename PointType::ScalarType zLength, + const _PointWriter& ptWriter) +{ + using ScalarType = typename PointType::ScalarType; + + const ScalarType x = 0.5 * xLength; + const ScalarType y = 0.5 * yLength; + const ScalarType z = 0.5 * zLength; + + ptWriter.Write(PointType( x, y, z)); + ptWriter.Write(PointType(-x, y, z)); + ptWriter.Write(PointType(-x, -y, z)); + ptWriter.Write(PointType( x, -y, z)); + ptWriter.Write(PointType(-x, -y, -z)); + ptWriter.Write(PointType(-x, y, -z)); + ptWriter.Write(PointType( x, y, -z)); + ptWriter.Write(PointType( x, -y, -z)); +} + +// Force-instantiate _GeneratePointsImpl for the supported point types. Only +// these instantiations will ever be needed due to the SFINAE machinery on the +// calling method template (the public GeneratePoints, in the header). +template GEOMUTIL_API void GeomUtilCuboidMeshGenerator::_GeneratePointsImpl( + const float, const float, const float, + const GeomUtilCuboidMeshGenerator::_PointWriter&); + +template GEOMUTIL_API void GeomUtilCuboidMeshGenerator::_GeneratePointsImpl( + const double, const double, const double, + const GeomUtilCuboidMeshGenerator::_PointWriter&); + + +PXR_NAMESPACE_CLOSE_SCOPE \ No newline at end of file diff --git a/pxr/imaging/geomUtil/cuboidMeshGenerator.h b/pxr/imaging/geomUtil/cuboidMeshGenerator.h new file mode 100644 index 0000000000..29c2260a38 --- /dev/null +++ b/pxr/imaging/geomUtil/cuboidMeshGenerator.h @@ -0,0 +1,101 @@ +// +// Copyright 2022 Pixar +// +// Licensed under the Apache License, Version 2.0 (the "Apache License") +// with the following modification; you may not use this file except in +// compliance with the Apache License and the following modification to it: +// Section 6. Trademarks. is deleted and replaced with: +// +// 6. Trademarks. This License does not grant permission to use the trade +// names, trademarks, service marks, or product names of the Licensor +// and its affiliates, except as required to comply with Section 4(c) of +// the License and to reproduce the content of the NOTICE file. +// +// You may obtain a copy of the Apache License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the Apache License with the above modification is +// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the Apache License for the specific +// language governing permissions and limitations under the Apache License. +// +#ifndef PXR_IMAGING_GEOM_UTIL_CUBOID_MESH_GENERATOR_H +#define PXR_IMAGING_GEOM_UTIL_CUBOID_MESH_GENERATOR_H + +#include "pxr/imaging/geomUtil/api.h" +#include "pxr/imaging/geomUtil/meshGeneratorBase.h" + +#include "pxr/pxr.h" + +PXR_NAMESPACE_OPEN_SCOPE + +class GfMatrix4d; +class PxOsdMeshTopology; + +/// This class provides an implementation for generating topology and point +/// positions on a rectangular cuboid given the dimensions along the X, Y and Z +/// axes. The generated cuboid is centered at the origin. +/// +/// An optional transform may be provided to GeneratePoints to orient the +/// cuboid as necessary. +/// +/// Usage: +/// \code{.cpp} +/// +/// const size_t numPoints = +/// GeomUtilCuboidMeshGenerator::ComputeNumPoints(); +/// const float l = 5, b = 4, h = 3; +/// +/// MyPointContainer points(numPoints); +/// +/// GeomUtilCuboidMeshGenerator::GeneratePoints( +/// points.begin(), l, b, h); +/// +/// \endcode +/// +class GeomUtilCuboidMeshGenerator final + : public GeomUtilMeshGeneratorBase +{ +public: + GEOMUTIL_API + static size_t ComputeNumPoints(); + + GEOMUTIL_API + static PxOsdMeshTopology GenerateTopology(); + + template::type> + static void GeneratePoints( + PointIterType iter, + const ScalarType xLength, + const ScalarType yLength, + const ScalarType zLength, + const GfMatrix4d* framePtr = nullptr) + { + using PointType = + typename std::iterator_traits::value_type; + + _GeneratePointsImpl(xLength, yLength, zLength, + framePtr ? _PointWriter(iter, framePtr) + : _PointWriter(iter)); + } + + using GeomUtilMeshGeneratorBase::GeneratePoints; + +private: + + template + static void _GeneratePointsImpl( + const typename PointType::ScalarType xLength, + const typename PointType::ScalarType yLength, + const typename PointType::ScalarType zLength, + const _PointWriter& ptWriter); +}; + +PXR_NAMESPACE_CLOSE_SCOPE + +#endif // PXR_IMAGING_GEOM_UTIL_CUBOID_MESH_GENERATOR_H diff --git a/pxr/imaging/geomUtil/cylinderMeshGenerator.cpp b/pxr/imaging/geomUtil/cylinderMeshGenerator.cpp new file mode 100644 index 0000000000..d11a5a867b --- /dev/null +++ b/pxr/imaging/geomUtil/cylinderMeshGenerator.cpp @@ -0,0 +1,151 @@ +// +// Copyright 2022 Pixar +// +// Licensed under the Apache License, Version 2.0 (the "Apache License") +// with the following modification; you may not use this file except in +// compliance with the Apache License and the following modification to it: +// Section 6. Trademarks. is deleted and replaced with: +// +// 6. Trademarks. This License does not grant permission to use the trade +// names, trademarks, service marks, or product names of the Licensor +// and its affiliates, except as required to comply with Section 4(c) of +// the License and to reproduce the content of the NOTICE file. +// +// You may obtain a copy of the Apache License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the Apache License with the above modification is +// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the Apache License for the specific +// language governing permissions and limitations under the Apache License. +// +#include "pxr/imaging/geomUtil/cylinderMeshGenerator.h" + +#include "pxr/imaging/pxOsd/meshTopology.h" +#include "pxr/imaging/pxOsd/tokens.h" + +#include "pxr/base/arch/math.h" +#include "pxr/base/vt/types.h" + +#include +#include +#include +#include + +PXR_NAMESPACE_OPEN_SCOPE + + +// static +size_t +GeomUtilCylinderMeshGenerator::ComputeNumPoints( + const size_t numRadial, + const bool closedSweep) +{ + if (numRadial < minNumRadial) { + return 0; + } + + const size_t numRadialPoints = + _ComputeNumRadialPoints(numRadial, closedSweep); + + return (4 * numRadialPoints) + 2; +} + +// static +PxOsdMeshTopology +GeomUtilCylinderMeshGenerator::GenerateTopology( + const size_t numRadial, + const bool closedSweep) +{ + if (numRadial < minNumRadial) { + return PxOsdMeshTopology(); + } + + return _GenerateCappedQuadTopology( + numRadial, + /* numQuadStrips = */ 1, + /* bottomCapStyle = */ CapStyleSeparateEdge, + /* topCapStyle = */ CapStyleSeparateEdge, + closedSweep); +} + +// static +template +void +GeomUtilCylinderMeshGenerator::_GeneratePointsImpl( + const size_t numRadial, + const typename PointType::ScalarType bottomRadius, + const typename PointType::ScalarType topRadius, + const typename PointType::ScalarType height, + const typename PointType::ScalarType sweepDegrees, + const _PointWriter& ptWriter) +{ + using ScalarType = typename PointType::ScalarType; + + if (numRadial < minNumRadial) { + return; + } + + const ScalarType twoPi = 2.0 * M_PI; + const ScalarType sweepRadians = + GfClamp((ScalarType) GfDegreesToRadians(sweepDegrees), -twoPi, twoPi); + const bool closedSweep = GfIsClose(std::abs(sweepRadians), twoPi, 1e-6); + + // Construct a circular arc of unit radius in the XY plane. + const size_t numRadialPoints = + _ComputeNumRadialPoints(numRadial, closedSweep); + std::vector> ringXY(numRadialPoints); + + for (size_t radIdx = 0; radIdx < numRadialPoints; ++radIdx) { + // Longitude range: [0, sweep] + const ScalarType longAngle = + (ScalarType(radIdx) / ScalarType(numRadial)) * (sweepRadians); + ringXY[radIdx][0] = cos(longAngle); + ringXY[radIdx][1] = sin(longAngle); + } + + const ScalarType zMax = 0.5 * height; + const ScalarType zMin = -zMax; + + // Bottom point: + ptWriter.Write(PointType(0.0, 0.0, zMin)); + + // Bottom rings; two consecutive rings at the same point locations, the + // first for the bottom triangle fan and the second for the main + // cylinder quads (for normals reasons the bottom "edge" is not shared): + for (size_t ringIdx = 0; ringIdx < 2; ++ringIdx) { + for (size_t radIdx = 0; radIdx < numRadialPoints; ++radIdx) { + ptWriter.Write(PointType(bottomRadius * ringXY[radIdx][0], + bottomRadius * ringXY[radIdx][1], + zMin)); + } + } + + // And another two rings, for the top edge. + for (size_t ringIdx = 0; ringIdx < 2; ++ringIdx) { + for (size_t radIdx = 0; radIdx < numRadialPoints; ++radIdx) { + ptWriter.Write(PointType(topRadius * ringXY[radIdx][0], + topRadius * ringXY[radIdx][1], + zMax)); + } + } + + // Top point: + ptWriter.Write(PointType(0.0, 0.0, zMax)); +} + +// Force-instantiate _GeneratePointsImpl for the supported point types. Only +// these instantiations will ever be needed due to the SFINAE machinery on the +// calling method template (the public GeneratePoints, in the header). +template GEOMUTIL_API void GeomUtilCylinderMeshGenerator::_GeneratePointsImpl( + const size_t, const float, const float, const float, const float, + const GeomUtilCylinderMeshGenerator::_PointWriter&); + +template GEOMUTIL_API void GeomUtilCylinderMeshGenerator::_GeneratePointsImpl( + const size_t, const double, const double, const double, const double, + const GeomUtilCylinderMeshGenerator::_PointWriter&); + + +PXR_NAMESPACE_CLOSE_SCOPE \ No newline at end of file diff --git a/pxr/imaging/geomUtil/cylinderMeshGenerator.h b/pxr/imaging/geomUtil/cylinderMeshGenerator.h new file mode 100644 index 0000000000..33cd7a4516 --- /dev/null +++ b/pxr/imaging/geomUtil/cylinderMeshGenerator.h @@ -0,0 +1,143 @@ +// +// Copyright 2022 Pixar +// +// Licensed under the Apache License, Version 2.0 (the "Apache License") +// with the following modification; you may not use this file except in +// compliance with the Apache License and the following modification to it: +// Section 6. Trademarks. is deleted and replaced with: +// +// 6. Trademarks. This License does not grant permission to use the trade +// names, trademarks, service marks, or product names of the Licensor +// and its affiliates, except as required to comply with Section 4(c) of +// the License and to reproduce the content of the NOTICE file. +// +// You may obtain a copy of the Apache License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the Apache License with the above modification is +// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the Apache License for the specific +// language governing permissions and limitations under the Apache License. +// +#ifndef PXR_IMAGING_GEOM_UTIL_CYLINDER_MESH_GENERATOR_H +#define PXR_IMAGING_GEOM_UTIL_CYLINDER_MESH_GENERATOR_H + +#include "pxr/imaging/geomUtil/api.h" +#include "pxr/imaging/geomUtil/meshGeneratorBase.h" + +#include "pxr/pxr.h" + +PXR_NAMESPACE_OPEN_SCOPE + +class GfMatrix4d; +class PxOsdMeshTopology; + +/// This class provides an implementation for generating topology and point +/// positions on a cylinder with a given radius and height. The cylinder is +/// made up of circular cross-sections in the XY plane and is centered at the +/// origin. Each cross-section has numRadial segments. The height is aligned +/// with the Z axis, with the base at Z = -h/2. +/// +/// An optional transform may be provided to GeneratePoints to orient the +/// cone as necessary (e.g., whose height is along the Y axis). +/// +/// An additional overload of GeneratePoints is provided to specify different +/// radii for the bottom and top discs of the cylinder and a sweep angle for +/// cylinder about the +Z axis. When the sweep is less than 360 degrees, the +/// generated geometry is not closed. +/// +/// \note Setting one radius to 0 in order to get a cone is inefficient and +/// could result in artifacts. Clients should use +/// GeomUtilConeMeshGenerator instead. +/// +/// Usage: +/// \code{.cpp} +/// +/// const size_t numRadial = 8; +/// const size_t numPoints = +/// GeomUtilCylinderMeshGenerator::ComputeNumPoints(numRadial); +/// const float radius = 1, height = 2; +/// +/// MyPointContainer points(numPoints); +/// +/// GeomUtilCylinderMeshGenerator::GeneratePoints( +/// points.begin(), numRadial, radius, height); +/// +/// \endcode +/// +class GeomUtilCylinderMeshGenerator final + : public GeomUtilMeshGeneratorBase +{ +public: + static constexpr size_t minNumRadial = 3; + + GEOMUTIL_API + static size_t ComputeNumPoints( + const size_t numRadial, + const bool closedSweep = true); + + GEOMUTIL_API + static PxOsdMeshTopology GenerateTopology( + const size_t numRadial, + const bool closedSweep = true); + + template::type> + static void GeneratePoints( + PointIterType iter, + const size_t numRadial, + const ScalarType radius, + const ScalarType height, + const GfMatrix4d* framePtr = nullptr) + { + constexpr ScalarType sweep = 360; + + GeneratePoints(iter, numRadial, + /* bottomRadius = */ radius, + /* topRadius = */ radius, + height, sweep, framePtr); + } + + template::type> + static void GeneratePoints( + PointIterType iter, + const size_t numRadial, + const ScalarType bottomRadius, + const ScalarType topRadius, + const ScalarType height, + const ScalarType sweepDegrees, + const GfMatrix4d* framePtr = nullptr) + { + using PointType = + typename std::iterator_traits::value_type; + + _GeneratePointsImpl(numRadial, bottomRadius, topRadius, height, + sweepDegrees, + framePtr ? _PointWriter(iter, framePtr) + : _PointWriter(iter)); + } + + using GeomUtilMeshGeneratorBase::GeneratePoints; + +private: + + template + static void _GeneratePointsImpl( + const size_t numRadial, + const typename PointType::ScalarType bottomRadius, + const typename PointType::ScalarType topRadius, + const typename PointType::ScalarType height, + const typename PointType::ScalarType sweep, + const _PointWriter& ptWriter); +}; + +PXR_NAMESPACE_CLOSE_SCOPE + +#endif // PXR_IMAGING_GEOM_UTIL_CYLINDER_MESH_GENERATOR_H diff --git a/pxr/imaging/geomUtil/meshGeneratorBase.cpp b/pxr/imaging/geomUtil/meshGeneratorBase.cpp new file mode 100644 index 0000000000..7b486b077d --- /dev/null +++ b/pxr/imaging/geomUtil/meshGeneratorBase.cpp @@ -0,0 +1,127 @@ +// +// Copyright 2022 Pixar +// +// Licensed under the Apache License, Version 2.0 (the "Apache License") +// with the following modification; you may not use this file except in +// compliance with the Apache License and the following modification to it: +// Section 6. Trademarks. is deleted and replaced with: +// +// 6. Trademarks. This License does not grant permission to use the trade +// names, trademarks, service marks, or product names of the Licensor +// and its affiliates, except as required to comply with Section 4(c) of +// the License and to reproduce the content of the NOTICE file. +// +// You may obtain a copy of the Apache License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the Apache License with the above modification is +// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the Apache License for the specific +// language governing permissions and limitations under the Apache License. +// +#include "pxr/imaging/geomUtil/meshGeneratorBase.h" + +#include "pxr/imaging/pxOsd/meshTopology.h" +#include "pxr/imaging/pxOsd/tokens.h" + +#include "pxr/base/tf/diagnostic.h" +#include "pxr/base/vt/types.h" + +PXR_NAMESPACE_OPEN_SCOPE + + +// static +PxOsdMeshTopology +GeomUtilMeshGeneratorBase::_GenerateCappedQuadTopology( + const size_t numRadial, + const size_t numQuadStrips, + const GeomUtilMeshGeneratorBase::_CapStyle bottomCapStyle, + const GeomUtilMeshGeneratorBase::_CapStyle topCapStyle, + const bool closedSweep) +{ + if (numRadial < 3) { + TF_CODING_ERROR("Invalid topology requested."); + return PxOsdMeshTopology(); + } + + const size_t numTriStrips = + (bottomCapStyle != CapStyleNone) + (topCapStyle != CapStyleNone); + const size_t numTris = numTriStrips * numRadial; + const size_t numQuads = numQuadStrips * numRadial; + + VtIntArray countsArray(numQuads + numTris); + VtIntArray indicesArray((4 * numQuads) + (3 * numTris)); + + // NOTE: When the surface is closed (sweep of 360 degrees), we ensure that + // the start and end points of each circular ring point are the same + // topologically. This in turn means that the number of points on a + // (closed) ring is one less than an (open) arc. + const size_t numRadialPts = _ComputeNumRadialPoints(numRadial, closedSweep); + size_t ptIdx = 0; + int* countsIt = countsArray.data(); + int* indicesIt = indicesArray.data(); + + // Bottom triangle fan, if requested: + if (bottomCapStyle != CapStyleNone) { + size_t bottomPtIdx = ptIdx++; + for (size_t radIdx = 0; radIdx < numRadial; ++radIdx) { + *countsIt++ = 3; + *indicesIt++ = ptIdx + ((radIdx + 1) % numRadialPts); + *indicesIt++ = ptIdx + radIdx; + *indicesIt++ = bottomPtIdx; + } + // Adjust the point index cursor if the edge isn't to be shared with the + // following quad strip. + if (bottomCapStyle == CapStyleSeparateEdge) { + ptIdx += numRadialPts; + } + } + + // Middle quads: + for (size_t stripIdx = 0; stripIdx < numQuadStrips; ++stripIdx) { + for (size_t radIdx = 0; radIdx < numRadial; ++radIdx) { + *countsIt++ = 4; + *indicesIt++ = ptIdx + radIdx; + *indicesIt++ = ptIdx + ((radIdx + 1) % numRadialPts); + *indicesIt++ = ptIdx + ((radIdx + 1) % numRadialPts) + numRadialPts; + *indicesIt++ = ptIdx + radIdx + numRadialPts; + } + ptIdx += numRadialPts; + } + + // Top triangle fan, if requested: + if (topCapStyle != CapStyleNone) { + // Adjust the point index cursor if the edge isn't to be shared with the + // preceeding quad strip. + if (topCapStyle == CapStyleSeparateEdge) { + ptIdx += numRadialPts; + } + size_t topPtIdx = ptIdx + numRadialPts; + for (size_t radIdx = 0; radIdx < numRadial; ++radIdx) { + *countsIt++ = 3; + *indicesIt++ = ptIdx + radIdx; + *indicesIt++ = ptIdx + ((radIdx + 1) % numRadialPts); + *indicesIt++ = topPtIdx; + } + } + + return PxOsdMeshTopology( + PxOsdOpenSubdivTokens->catmullClark, + PxOsdOpenSubdivTokens->rightHanded, + countsArray, indicesArray); +} + +// static +size_t +GeomUtilMeshGeneratorBase::_ComputeNumRadialPoints( + const size_t numRadial, + const bool closedSweep) +{ + // For a ring, the first and last points are the same. For topological + // correctness, do not regenerate the same point. + return closedSweep? numRadial : numRadial + 1; +} + +PXR_NAMESPACE_CLOSE_SCOPE \ No newline at end of file diff --git a/pxr/imaging/geomUtil/meshGeneratorBase.h b/pxr/imaging/geomUtil/meshGeneratorBase.h new file mode 100644 index 0000000000..7a8bdf03ea --- /dev/null +++ b/pxr/imaging/geomUtil/meshGeneratorBase.h @@ -0,0 +1,247 @@ +// +// Copyright 2022 Pixar +// +// Licensed under the Apache License, Version 2.0 (the "Apache License") +// with the following modification; you may not use this file except in +// compliance with the Apache License and the following modification to it: +// Section 6. Trademarks. is deleted and replaced with: +// +// 6. Trademarks. This License does not grant permission to use the trade +// names, trademarks, service marks, or product names of the Licensor +// and its affiliates, except as required to comply with Section 4(c) of +// the License and to reproduce the content of the NOTICE file. +// +// You may obtain a copy of the Apache License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the Apache License with the above modification is +// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the Apache License for the specific +// language governing permissions and limitations under the Apache License. +// +#ifndef PXR_IMAGING_GEOM_UTIL_MESH_GENERATOR_BASE_H +#define PXR_IMAGING_GEOM_UTIL_MESH_GENERATOR_BASE_H + +#include "pxr/imaging/geomUtil/api.h" + +#include "pxr/base/gf/matrix4d.h" +#include "pxr/base/gf/vec3d.h" +#include "pxr/base/gf/vec3f.h" + +#include "pxr/pxr.h" + +#include +#include + +PXR_NAMESPACE_OPEN_SCOPE + +class PxOsdMeshTopology; + +/// This class provides common implementation for the different mesh generator +/// classes in GeomUtil. As the mesh generators are entirely implemented as +/// static functions, this "base class" is more of a grouping and access control +/// mechanism than a base class in the polymorphic sense. +/// +/// The mesh generator subclasses all follow a common pattern, providing static +/// methods for generating topology and point positions for their specific +/// geometric primitive. The data produced by these classes is only guaranteed +/// to be suitable for imaging the described surface; it is only one of many +/// possible interpretations of the surface, and should not be relied upon for +/// any other use. The generators may e.g. change the topology or ordering of +/// the produced data at any time. In short: these utilities are meant only to +/// be used to produce a blob of semi-blind data, for feeding to an imager that +/// supports PxOsdMeshTopology. +/// +/// The generators make use of templates and SFINAE to allow clients to pass any +/// output iterator that dereferences to either a GfVec3f or GfVec3d to their +/// GeneratePoints(...) method, and internally perform compile-time type erasure +/// in order to allow the implementations of their algorithms to be private +/// implementation detail, not defined in the headers. Although it's expected +/// that clients will typically want their point data in VtVec3fArray, the +/// described implementation was chosen to minimize the chance that any +/// prospective client with unusual data management requirements would be unable +/// to make use of the generators, or would be forced to resort to a container +/// copy in order to do so. +/// +/// The only public API on this class is a static GeneratePoints(...) template +/// method, intended to be added by subclasses to their GeneratePoints(...) +/// overload sets. It serves as the "error case" for callers that attempt to +/// pass iterators of unsupported types to the GeneratePoints(...) methods each +/// subclass declares. As all generator subclasses have this possibility and +/// the implementation requires SFINAE, it's implemented here and shared between +/// all subclasses. However, it's important that subclasses explicitly include +/// a "using" statement for the fallback to be included in overload resolution. +/// +class GeomUtilMeshGeneratorBase +{ +private: + + // Delete the implicit default c'tor. This class and its subclasses are + // only for grouping; there's never any need to make instances. + GeomUtilMeshGeneratorBase() = delete; + +protected: + + // SFINAE helper types, for more compact and readable template shenanigans + // in the subclasses. + template + struct _IsGfVec3Iterator + { + using PointType = typename std::iterator_traits::value_type; + static constexpr bool value = + std::is_same::value || + std::is_same::value; + }; + + template + struct _EnableIfGfVec3Iterator + : public std::enable_if<_IsGfVec3Iterator::value, void> + {}; + + template + struct _EnableIfNotGfVec3Iterator + : public std::enable_if::value, void> + {}; + + // Helper struct to provide iterator type erasure, allowing subclasses to + // implement their GeneratePoints methods privately. Usage doesn't require + // any heap allocation or virtual dispatch/runtime typing. In addition to + // erasing the iterator type, this also provides a convenient way to allow + // subclasses to offer GeneratePoints methods that can apply an additional + // frame transform without having to actually plumb that detail into the + // guts of their point generator code. + // + // Note: Ensuring the interoperability of the PointType with the IterType + // used at construction is the responsibility of the client. It's typically + // guaranteed by the client deriving PointType from IterType; see subclass + // use for examples and how they guarantee IterType dereferences to a + // supportable point type. + template + struct _PointWriter + { + template + _PointWriter( + IterType& iter) + : _writeFnPtr(&_PointWriter::_WritePoint) + , _untypedIterPtr(static_cast(&iter)) + {} + + template + _PointWriter( + IterType& iter, + const GfMatrix4d* const framePtr) + : _writeFnPtr( + &_PointWriter::_TransformAndWritePoint) + , _untypedIterPtr(static_cast(&iter)) + , _framePtr(framePtr) + {} + + void Write( + const PointType& pt) const + { + (this->*_writeFnPtr)(pt); + } + + private: + template + void _WritePoint( + const PointType& pt) const + { + IterType& iter = *static_cast(_untypedIterPtr); + *iter = pt; + ++iter; + } + + template + void _TransformAndWritePoint( + const PointType& pt) const + { + IterType& iter = *static_cast(_untypedIterPtr); + *iter = _framePtr->Transform(pt); + ++iter; + } + + using _WriteFnPtr = + void (_PointWriter::*)(const PointType &) const; + _WriteFnPtr _writeFnPtr; + void* _untypedIterPtr; + const GfMatrix4d* _framePtr; + }; + + // Common topology helper method. + // + // Several of the subclasses make use of a common topology, specifically "a + // triangle fan around a 'bottom' point, some number of quad strips forming + // rings with shared edges, and another triangle fan surrounding a 'top' + // point." The two triangle fans can be considered "caps" on a "tube" of + // linked quad strips. This triangle fans + quad strips topology also + // describes the latitude/longitude topology of the globe, as another + // example. + // + // Because we currently rely on downstream machinery to infer surface + // normals from the topology, we sometimes want the "caps" to share their + // edge-ring with the adjacent quad strip, and other times need that edge- + // ring to not be shared between the "cap" and "body" surfaces. The edges + // are coincident in space but the surface is not continuous across that + // edge. + // + // Subclasses specify the "cap" conditions they require to support the + // surface-continuity condition described above, and other uses where a + // "cap" is not needed (e.g. the point-end of a cone). + // + // Subclasses also specify whether the surface is closed or open. This + // is typically exposed via a sweep parameter, wherein a sweep of a multiple + // of 2 * pi results in a "closed" surface. The generated points and by + // extension, the generated topology, differs for "open" and "closed" + // surfaces. + // + enum _CapStyle { + CapStyleNone, + CapStyleSharedEdge, + CapStyleSeparateEdge + }; + + static PxOsdMeshTopology _GenerateCappedQuadTopology( + const size_t numRadial, + const size_t numQuadStrips, + const _CapStyle bottomCapStyle, + const _CapStyle topCapStyle, + const bool closedSweep); + + + // Subclasses that use the topology helper method above generate one or more + // circular arcs during point generation. The number of radial points on + // each arc depends on the number of radial segments and whether the arc + // is fully swept (i.e., a ring). + static size_t _ComputeNumRadialPoints( + const size_t numRadial, + const bool closedSweep); + +public: + + // This template provides a "fallback" for GeneratePoints(...) calls that + // do not meet the SFINAE requirement that the given point-container- + // iterator must dereference to a GfVec3f or GfVec3d. This version + // generates a helpful compile time assertion in such a scenario. As noted + // earlier, subclasses should explicitly add a "using" statement with + // this method to include it in overload resolution. + // + template::type> + static void GeneratePoints( + PointIterType iter, ...) + { + static_assert(_IsGfVec3Iterator::value, + "This function only supports iterators to GfVec3f or GfVec3d " + "objects."); + } + +}; + + +PXR_NAMESPACE_CLOSE_SCOPE + +#endif // PXR_IMAGING_GEOM_UTIL_MESH_GENERATOR_BASE_H diff --git a/pxr/imaging/geomUtil/module.cpp b/pxr/imaging/geomUtil/module.cpp new file mode 100644 index 0000000000..0fd58ee758 --- /dev/null +++ b/pxr/imaging/geomUtil/module.cpp @@ -0,0 +1,36 @@ +// +// Copyright 2022 Pixar +// +// Licensed under the Apache License, Version 2.0 (the "Apache License") +// with the following modification; you may not use this file except in +// compliance with the Apache License and the following modification to it: +// Section 6. Trademarks. is deleted and replaced with: +// +// 6. Trademarks. This License does not grant permission to use the trade +// names, trademarks, service marks, or product names of the Licensor +// and its affiliates, except as required to comply with Section 4(c) of +// the License and to reproduce the content of the NOTICE file. +// +// You may obtain a copy of the Apache License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the Apache License with the above modification is +// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the Apache License for the specific +// language governing permissions and limitations under the Apache License. +// +#include "pxr/pxr.h" +#include "pxr/base/tf/pyModule.h" + +PXR_NAMESPACE_USING_DIRECTIVE + +TF_WRAP_MODULE +{ + TF_WRAP(CapsuleMeshGenerator); + TF_WRAP(ConeMeshGenerator); + TF_WRAP(CuboidMeshGenerator); + TF_WRAP(CylinderMeshGenerator); + TF_WRAP(SphereMeshGenerator); +} diff --git a/pxr/imaging/geomUtil/moduleDeps.cpp b/pxr/imaging/geomUtil/moduleDeps.cpp new file mode 100644 index 0000000000..a06c6233cb --- /dev/null +++ b/pxr/imaging/geomUtil/moduleDeps.cpp @@ -0,0 +1,50 @@ +// +// Copyright 2016 Pixar +// +// Licensed under the Apache License, Version 2.0 (the "Apache License") +// with the following modification; you may not use this file except in +// compliance with the Apache License and the following modification to it: +// Section 6. Trademarks. is deleted and replaced with: +// +// 6. Trademarks. This License does not grant permission to use the trade +// names, trademarks, service marks, or product names of the Licensor +// and its affiliates, except as required to comply with Section 4(c) of +// the License and to reproduce the content of the NOTICE file. +// +// You may obtain a copy of the Apache License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the Apache License with the above modification is +// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the Apache License for the specific +// language governing permissions and limitations under the Apache License. +// +//////////////////////////////////////////////////////////////////////// + +#include "pxr/pxr.h" +#include "pxr/base/tf/registryManager.h" +#include "pxr/base/tf/scriptModuleLoader.h" +#include "pxr/base/tf/token.h" + +#include + +PXR_NAMESPACE_OPEN_SCOPE + +TF_REGISTRY_FUNCTION(TfScriptModuleLoader) { + // List of direct dependencies for this library. + const std::vector reqs = { + TfToken("arch"), + TfToken("gf"), + TfToken("pxOsd"), + TfToken("tf"), + TfToken("vt") + }; + TfScriptModuleLoader::GetInstance(). + RegisterLibrary(TfToken("geomUtil"), TfToken("pxr.GeomUtil"), reqs); +} + +PXR_NAMESPACE_CLOSE_SCOPE + + diff --git a/pxr/imaging/geomUtil/pch.h b/pxr/imaging/geomUtil/pch.h new file mode 100644 index 0000000000..0eb05452df --- /dev/null +++ b/pxr/imaging/geomUtil/pch.h @@ -0,0 +1,137 @@ +// +// Copyright 2022 Pixar +// +// Licensed under the Apache License, Version 2.0 (the "Apache License") +// with the following modification; you may not use this file except in +// compliance with the Apache License and the following modification to it: +// Section 6. Trademarks. is deleted and replaced with: +// +// 6. Trademarks. This License does not grant permission to use the trade +// names, trademarks, service marks, or product names of the Licensor +// and its affiliates, except as required to comply with Section 4(c) of +// the License and to reproduce the content of the NOTICE file. +// +// You may obtain a copy of the Apache License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the Apache License with the above modification is +// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the Apache License for the specific +// language governing permissions and limitations under the Apache License. +// +// WARNING: THIS FILE IS GENERATED. DO NOT EDIT. +// + +#define TF_MAX_ARITY 7 +#include "pxr/pxr.h" +#include "pxr/base/arch/defines.h" +#if defined(ARCH_OS_WINDOWS) +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif + +#include +#include +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef PXR_PYTHON_SUPPORT_ENABLED +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(__APPLE__) // Fix breakage caused by Python's pyport.h. +#undef tolower +#undef toupper +#endif +#endif // PXR_PYTHON_SUPPORT_ENABLED +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef PXR_PYTHON_SUPPORT_ENABLED +#include +#endif // PXR_PYTHON_SUPPORT_ENABLED diff --git a/pxr/imaging/geomUtil/sphereMeshGenerator.cpp b/pxr/imaging/geomUtil/sphereMeshGenerator.cpp new file mode 100644 index 0000000000..2ca93b3755 --- /dev/null +++ b/pxr/imaging/geomUtil/sphereMeshGenerator.cpp @@ -0,0 +1,145 @@ +// +// Copyright 2022 Pixar +// +// Licensed under the Apache License, Version 2.0 (the "Apache License") +// with the following modification; you may not use this file except in +// compliance with the Apache License and the following modification to it: +// Section 6. Trademarks. is deleted and replaced with: +// +// 6. Trademarks. This License does not grant permission to use the trade +// names, trademarks, service marks, or product names of the Licensor +// and its affiliates, except as required to comply with Section 4(c) of +// the License and to reproduce the content of the NOTICE file. +// +// You may obtain a copy of the Apache License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the Apache License with the above modification is +// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the Apache License for the specific +// language governing permissions and limitations under the Apache License. +// +#include "pxr/imaging/geomUtil/sphereMeshGenerator.h" + +#include "pxr/imaging/pxOsd/meshTopology.h" +#include "pxr/imaging/pxOsd/tokens.h" + +#include "pxr/base/arch/math.h" +#include "pxr/base/vt/types.h" + +#include +#include +#include +#include + +PXR_NAMESPACE_OPEN_SCOPE + + +// static +size_t +GeomUtilSphereMeshGenerator::ComputeNumPoints( + const size_t numRadial, + const size_t numAxial, + const bool closedSweep) +{ + if ((numRadial < minNumRadial) || (numAxial < minNumAxial)) { + return 0; + } + + const size_t numRadialPoints = + _ComputeNumRadialPoints(numRadial, closedSweep); + + return((numAxial - 1) * numRadialPoints) + 2; +} + +// static +PxOsdMeshTopology +GeomUtilSphereMeshGenerator::GenerateTopology( + const size_t numRadial, + const size_t numAxial, + const bool closedSweep) +{ + if ((numRadial < minNumRadial) || (numAxial < minNumAxial)) { + return PxOsdMeshTopology(); + } + + return _GenerateCappedQuadTopology( + numRadial, + /* numQuadStrips = */ numAxial - 2, + /* bottomCapStyle = */ CapStyleSharedEdge, + /* topCapStyle = */ CapStyleSharedEdge, + closedSweep); +} + +// static +template +void +GeomUtilSphereMeshGenerator::_GeneratePointsImpl( + const size_t numRadial, + const size_t numAxial, + const typename PointType::ScalarType radius, + const typename PointType::ScalarType sweepDegrees, + const _PointWriter& ptWriter) +{ + using ScalarType = typename PointType::ScalarType; + + if ((numRadial < minNumRadial) || (numAxial < minNumAxial)) { + return; + } + + const ScalarType twoPi = 2.0 * M_PI; + const ScalarType sweepRadians = + GfClamp((ScalarType) GfDegreesToRadians(sweepDegrees), -twoPi, twoPi); + const bool closedSweep = GfIsClose(std::abs(sweepRadians), twoPi, 1e-6); + + // Construct a circular arc/ring of the specified radius in the XY plane. + const size_t numRadialPoints = + _ComputeNumRadialPoints(numRadial, closedSweep); + std::vector> ringXY(numRadialPoints); + + for (size_t radIdx = 0; radIdx < numRadialPoints; ++radIdx) { + // Longitude range: [0, sweep] + const ScalarType longAngle = + (ScalarType(radIdx) / ScalarType(numRadial)) * sweepRadians; + ringXY[radIdx][0] = radius * cos(longAngle); + ringXY[radIdx][1] = radius * sin(longAngle); + } + + // Bottom point: + ptWriter.Write(PointType(0.0, 0.0, -radius)); + + // Latitude rings: + for (size_t axIdx = 1; axIdx < numAxial; ++axIdx) { + // Latitude range: (-0.5pi, 0.5pi) + const ScalarType latAngle = + ((ScalarType(axIdx) / ScalarType(numAxial)) - 0.5) * M_PI; + + const ScalarType radScale = cos(latAngle); + const ScalarType latitude = radius * sin(latAngle); + + for (size_t radIdx = 0; radIdx < numRadialPoints; ++radIdx) { + ptWriter.Write(PointType(radScale * ringXY[radIdx][0], + radScale * ringXY[radIdx][1], + latitude)); + } + } + + // Top point: + ptWriter.Write(PointType(0.0, 0.0, radius)); +} + +// Force-instantiate _GeneratePointsImpl for the supported point types. Only +// these instantiations will ever be needed due to the SFINAE machinery on the +// calling method template (the public GeneratePoints, in the header). +template GEOMUTIL_API void GeomUtilSphereMeshGenerator::_GeneratePointsImpl( + const size_t, const size_t, const float, const float, + const GeomUtilSphereMeshGenerator::_PointWriter&); + +template GEOMUTIL_API void GeomUtilSphereMeshGenerator::_GeneratePointsImpl( + const size_t, const size_t, const double, const double, + const GeomUtilSphereMeshGenerator::_PointWriter&); + + +PXR_NAMESPACE_CLOSE_SCOPE \ No newline at end of file diff --git a/pxr/imaging/geomUtil/sphereMeshGenerator.h b/pxr/imaging/geomUtil/sphereMeshGenerator.h new file mode 100644 index 0000000000..8393fd842c --- /dev/null +++ b/pxr/imaging/geomUtil/sphereMeshGenerator.h @@ -0,0 +1,135 @@ +// +// Copyright 2022 Pixar +// +// Licensed under the Apache License, Version 2.0 (the "Apache License") +// with the following modification; you may not use this file except in +// compliance with the Apache License and the following modification to it: +// Section 6. Trademarks. is deleted and replaced with: +// +// 6. Trademarks. This License does not grant permission to use the trade +// names, trademarks, service marks, or product names of the Licensor +// and its affiliates, except as required to comply with Section 4(c) of +// the License and to reproduce the content of the NOTICE file. +// +// You may obtain a copy of the Apache License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the Apache License with the above modification is +// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the Apache License for the specific +// language governing permissions and limitations under the Apache License. +// +#ifndef PXR_IMAGING_GEOM_UTIL_SPHERE_MESH_GENERATOR_H +#define PXR_IMAGING_GEOM_UTIL_SPHERE_MESH_GENERATOR_H + +#include "pxr/imaging/geomUtil/api.h" +#include "pxr/imaging/geomUtil/meshGeneratorBase.h" + +#include "pxr/pxr.h" + +PXR_NAMESPACE_OPEN_SCOPE + +class GfMatrix4d; +class PxOsdMeshTopology; + +/// This class provides an implementation for generating topology and point +/// positions on a sphere with a given radius. The sphere is made up of +/// circular cross-sections in the XY plane and is centered at the origin. +/// Each cross-section has numRadial segments. Successive cross-sections are +/// generated at numAxial locations along the Z axis, with the bottom of the +/// sphere at Z = -r and top at Z = r. +/// +/// An optional transform may be provided to GeneratePoints to orient the +/// sphere as necessary (e.g., cross-sections in the YZ plane). +/// +/// An additional overload of GeneratePoints is provided to specify a sweep +/// angle for the sphere about the +Z axis. When the sweep is less than 360 +/// degrees, the generated geometry is not closed. +/// +/// Usage: +/// \code{.cpp} +/// +/// const size_t numRadial = 4, numAxial = 4; +/// const size_t numPoints = +/// GeomUtilSphereMeshGenerator::ComputeNumPoints(numRadial, numAxial); +/// const float radius = 5; +/// +/// MyPointContainer points(numPoints); +/// +/// GeomUtilSphereMeshGenerator::GeneratePoints( +/// points.begin(), numRadial, numAxial, radius); +/// +/// \endcode +/// +class GeomUtilSphereMeshGenerator final + : public GeomUtilMeshGeneratorBase +{ +public: + static constexpr size_t minNumRadial = 3; + static constexpr size_t minNumAxial = 2; + + GEOMUTIL_API + static size_t ComputeNumPoints( + const size_t numRadial, + const size_t numAxial, + const bool closedSweep = true); + + GEOMUTIL_API + static PxOsdMeshTopology GenerateTopology( + const size_t numRadial, + const size_t numAxial, + const bool closedSweep = true); + + template::type> + static void GeneratePoints( + PointIterType iter, + const size_t numRadial, + const size_t numAxial, + const ScalarType radius, + const GfMatrix4d* framePtr = nullptr) + { + constexpr ScalarType sweep = 360; + GeneratePoints(iter, numRadial, numAxial, radius, sweep, framePtr); + } + + template::type> + static void GeneratePoints( + PointIterType iter, + const size_t numRadial, + const size_t numAxial, + const ScalarType radius, + const ScalarType sweepDegrees, + const GfMatrix4d* framePtr = nullptr) + { + using PointType = + typename std::iterator_traits::value_type; + + _GeneratePointsImpl(numRadial, numAxial, radius, sweepDegrees, + framePtr ? _PointWriter(iter, framePtr) + : _PointWriter(iter)); + } + + using GeomUtilMeshGeneratorBase::GeneratePoints; + +private: + + template + static void _GeneratePointsImpl( + const size_t numRadial, + const size_t numAxial, + const typename PointType::ScalarType radius, + const typename PointType::ScalarType sweepDegrees, + const _PointWriter& ptWriter); +}; + +PXR_NAMESPACE_CLOSE_SCOPE + +#endif // PXR_IMAGING_GEOM_UTIL_SPHERE_MESH_GENERATOR_H diff --git a/pxr/imaging/geomUtil/testenv/testMeshGenerators.cpp b/pxr/imaging/geomUtil/testenv/testMeshGenerators.cpp new file mode 100644 index 0000000000..3321dccd0a --- /dev/null +++ b/pxr/imaging/geomUtil/testenv/testMeshGenerators.cpp @@ -0,0 +1,249 @@ +// +// Copyright 2022 Pixar +// +// Licensed under the Apache License, Version 2.0 (the "Apache License") +// with the following modification; you may not use this file except in +// compliance with the Apache License and the following modification to it: +// Section 6. Trademarks. is deleted and replaced with: +// +// 6. Trademarks. This License does not grant permission to use the trade +// names, trademarks, service marks, or product names of the Licensor +// and its affiliates, except as required to comply with Section 4(c) of +// the License and to reproduce the content of the NOTICE file. +// +// You may obtain a copy of the Apache License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the Apache License with the above modification is +// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the Apache License for the specific +// language governing permissions and limitations under the Apache License. +// + +#include "pxr/pxr.h" + +#include "pxr/imaging/geomUtil/capsuleMeshGenerator.h" +#include "pxr/imaging/geomUtil/coneMeshGenerator.h" +#include "pxr/imaging/geomUtil/cuboidMeshGenerator.h" +#include "pxr/imaging/geomUtil/cylinderMeshGenerator.h" +#include "pxr/imaging/geomUtil/sphereMeshGenerator.h" +#include "pxr/imaging/pxOsd/meshTopology.h" + +#include "pxr/base/tf/errorMark.h" +#include "pxr/base/gf/vec3d.h" +#include "pxr/base/gf/vec3f.h" +#include "pxr/base/vt/array.h" + +#include +#include + +PXR_NAMESPACE_USING_DIRECTIVE; + +namespace { + +static void +_LogHeader(std::string const &msg, std::ofstream &out) +{ + out << msg << std::endl; + out << std::string(msg.length(), '-') << std::endl; +} + +static void +_LogFooter(std::ofstream &out) +{ + out << std::endl << std::endl; +} + +template +static void +_Log(PxOsdMeshTopology const &topology, + VtArray const &points, + std::ofstream &out) +{ + out << "Topology:\n"; + out << " " << topology << std::endl << std::endl; + + out << "Points:\n"; + out << " " << points << std::endl << std::endl; +} + +} + +static bool TestTopologyAndPointGeneration( + const float sweep, std::ofstream &out) +{ + const bool closedSweep = + GfIsClose(cos(GfDegreesToRadians(sweep)), 1.0, 1e-4); + { + _LogHeader("1. Capsule", out); + + using MeshGen = GeomUtilCapsuleMeshGenerator; + + const size_t numRadial = 10, numCapAxial = 4; + const float radius = 0.5, height = 2; + + out << "radius = " << radius + << ", height = " << height + << ", sweep = " << sweep + << std::endl << std::endl; + + const PxOsdMeshTopology topology = + MeshGen::GenerateTopology(numRadial, numCapAxial, closedSweep); + + const size_t numPoints = + MeshGen::ComputeNumPoints(numRadial, numCapAxial, closedSweep); + VtVec3fArray points(numPoints); + if (closedSweep) { + MeshGen::GeneratePoints( + points.begin(), numRadial, numCapAxial, radius, height); + + } else { + MeshGen::GeneratePoints( + points.begin(), numRadial, numCapAxial, + /* bottomRadius = */ radius, + /* topRadius = */ radius, + height, + /* bottomCapHeight = */ radius, + /* topCapHeight = */ radius, + sweep); + } + + _Log(topology, points, out); + + _LogFooter(out); + } + + { + _LogHeader("2. Cone", out); + + using MeshGen = GeomUtilConeMeshGenerator; + + const size_t numRadial = 10; + const float radius = 0.5, height = 2; + + out << "radius = " << radius + << ", height = " << height + << ", sweep = " << sweep + << std::endl << std::endl; + + const PxOsdMeshTopology topology = + MeshGen::GenerateTopology(numRadial, closedSweep); + + const size_t numPoints = + MeshGen::ComputeNumPoints(numRadial, closedSweep); + VtVec3fArray points(numPoints); + MeshGen::GeneratePoints( + points.begin(), numRadial, radius, height, sweep); + + _Log(topology, points, out); + + _LogFooter(out); + } + + { + _LogHeader("3. Cube", out); + + using MeshGen = GeomUtilCuboidMeshGenerator; + + const float side = 1.0; + + out << "side = " << side << std::endl << std::endl; + + const PxOsdMeshTopology topology = MeshGen::GenerateTopology(); + + const size_t numPoints = MeshGen::ComputeNumPoints(); + VtVec3fArray points(numPoints); + MeshGen::GeneratePoints(points.begin(), side, side, side); + + _Log(topology, points, out); + + _LogFooter(out); + } + + { + _LogHeader("4. Cylinder", out); + + using MeshGen = GeomUtilCylinderMeshGenerator; + + const size_t numRadial = 10; + const float radius = 0.5, height = 2; + + out << "radius = " << radius + << ", height = " << height + << ", sweep = " << sweep + << std::endl << std::endl; + + const PxOsdMeshTopology topology = + MeshGen::GenerateTopology(numRadial, closedSweep); + + const size_t numPoints = + MeshGen::ComputeNumPoints(numRadial, closedSweep); + VtVec3fArray points(numPoints); + if (closedSweep) { + MeshGen::GeneratePoints( + points.begin(), numRadial, radius, height); + + } else { + MeshGen::GeneratePoints( + points.begin(), numRadial, + /* bottomRadius = */ radius, + /* topRadius = */ radius, + height, sweep); + } + + _Log(topology, points, out); + + _LogFooter(out); + } + + { + _LogHeader("5. Sphere", out); + + using MeshGen = GeomUtilSphereMeshGenerator; + + const size_t numRadial = 10, numAxial = 10; + const float radius = 0.5; + + out << "radius = " << radius + << ", sweep = " << sweep + << std::endl << std::endl; + + const PxOsdMeshTopology topology = + MeshGen::GenerateTopology(numRadial, numAxial, closedSweep); + + const size_t numPoints = + MeshGen::ComputeNumPoints(numRadial, numAxial, closedSweep); + VtVec3fArray points(numPoints); + MeshGen::GeneratePoints( + points.begin(), numRadial, numAxial, radius, sweep); + + _Log(topology, points, out); + + _LogFooter(out); + } + + return true; +} + +int main() +{ + TfErrorMark mark; + + std::ofstream out1("generatedMeshes_closed.txt"); + std::ofstream out2("generatedMeshes_open.txt"); + + bool success = TestTopologyAndPointGeneration(/*sweep = */ 360, out1) + && TestTopologyAndPointGeneration(/*sweep = */ 120, out2); + + TF_VERIFY(mark.IsClean()); + + if (success && mark.IsClean()) { + std::cout << "OK" << std::endl; + return EXIT_SUCCESS; + } else { + std::cout << "FAILED" << std::endl; + return EXIT_FAILURE; + } +} diff --git a/pxr/imaging/geomUtil/testenv/testMeshGenerators/baseline/generatedMeshes_closed.txt b/pxr/imaging/geomUtil/testenv/testMeshGenerators/baseline/generatedMeshes_closed.txt new file mode 100644 index 0000000000..f614a5b0ce --- /dev/null +++ b/pxr/imaging/geomUtil/testenv/testMeshGenerators/baseline/generatedMeshes_closed.txt @@ -0,0 +1,60 @@ +1. Capsule +---------- +radius = 0.5, height = 2, sweep = 360 + +Topology: + (rightHanded, catmullClark, ([3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3]), ([2, 1, 0, 3, 2, 0, 4, 3, 0, 5, 4, 0, 6, 5, 0, 7, 6, 0, 8, 7, 0, 9, 8, 0, 10, 9, 0, 1, 10, 0, 1, 2, 12, 11, 2, 3, 13, 12, 3, 4, 14, 13, 4, 5, 15, 14, 5, 6, 16, 15, 6, 7, 17, 16, 7, 8, 18, 17, 8, 9, 19, 18, 9, 10, 20, 19, 10, 1, 11, 20, 11, 12, 22, 21, 12, 13, 23, 22, 13, 14, 24, 23, 14, 15, 25, 24, 15, 16, 26, 25, 16, 17, 27, 26, 17, 18, 28, 27, 18, 19, 29, 28, 19, 20, 30, 29, 20, 11, 21, 30, 21, 22, 32, 31, 22, 23, 33, 32, 23, 24, 34, 33, 24, 25, 35, 34, 25, 26, 36, 35, 26, 27, 37, 36, 27, 28, 38, 37, 28, 29, 39, 38, 29, 30, 40, 39, 30, 21, 31, 40, 31, 32, 42, 41, 32, 33, 43, 42, 33, 34, 44, 43, 34, 35, 45, 44, 35, 36, 46, 45, 36, 37, 47, 46, 37, 38, 48, 47, 38, 39, 49, 48, 39, 40, 50, 49, 40, 31, 41, 50, 41, 42, 52, 51, 42, 43, 53, 52, 43, 44, 54, 53, 44, 45, 55, 54, 45, 46, 56, 55, 46, 47, 57, 56, 47, 48, 58, 57, 48, 49, 59, 58, 49, 50, 60, 59, 50, 41, 51, 60, 51, 52, 62, 61, 52, 53, 63, 62, 53, 54, 64, 63, 54, 55, 65, 64, 55, 56, 66, 65, 56, 57, 67, 66, 57, 58, 68, 67, 58, 59, 69, 68, 59, 60, 70, 69, 60, 51, 61, 70, 61, 62, 72, 71, 62, 63, 73, 72, 63, 64, 74, 73, 64, 65, 75, 74, 65, 66, 76, 75, 66, 67, 77, 76, 67, 68, 78, 77, 68, 69, 79, 78, 69, 70, 80, 79, 70, 61, 71, 80, 71, 72, 81, 72, 73, 81, 73, 74, 81, 74, 75, 81, 75, 76, 81, 76, 77, 81, 77, 78, 81, 78, 79, 81, 79, 80, 81, 80, 71, 81]), ([])) + +Points: + [(0, 0, -1.5), (0.19134171, 0, -1.4619398), (0.1547987, 0.11246783, -1.4619398), (0.059127837, 0.18197678, -1.4619398), (-0.05912787, 0.18197678, -1.4619398), (-0.15479872, 0.112467825, -1.4619398), (-0.19134171, -1.6727624e-8, -1.4619398), (-0.15479866, -0.11246789, -1.4619398), (-0.05912786, -0.18197678, -1.4619398), (0.059127867, -0.18197678, -1.4619398), (0.15479869, -0.11246785, -1.4619398), (0.35355338, 0, -1.3535534), (0.2860307, 0.20781346, -1.3535534), (0.109253995, 0.33624926, -1.3535534), (-0.10925406, 0.33624923, -1.3535534), (-0.2860307, 0.20781344, -1.3535534), (-0.35355338, -3.090862e-8, -1.3535534), (-0.28603062, -0.20781356, -1.3535534), (-0.10925404, -0.33624923, -1.3535534), (0.10925405, -0.33624923, -1.3535534), (0.28603068, -0.20781349, -1.3535534), (0.46193975, 0, -1.1913418), (0.3737171, 0.27152136, -1.1913418), (0.14274722, 0.43933082, -1.1913418), (-0.14274731, 0.4393308, -1.1913418), (-0.37371713, 0.27152133, -1.1913418), (-0.46193975, -4.0384055e-8, -1.1913418), (-0.373717, -0.2715215, -1.1913418), (-0.14274728, -0.4393308, -1.1913418), (0.1427473, -0.4393308, -1.1913418), (0.3737171, -0.2715214, -1.1913418), (0.5, 0, -1), (0.4045085, 0.29389262, -1), (0.15450849, 0.47552827, -1), (-0.15450858, 0.47552824, -1), (-0.40450853, 0.2938926, -1), (-0.5, -4.371139e-8, -1), (-0.40450838, -0.29389277, -1), (-0.15450855, -0.47552824, -1), (0.15450856, -0.47552824, -1), (0.40450847, -0.29389265, -1), (0.5, 0, 1), (0.4045085, 0.29389262, 1), (0.15450849, 0.47552827, 1), (-0.15450858, 0.47552824, 1), (-0.40450853, 0.2938926, 1), (-0.5, -4.371139e-8, 1), (-0.40450838, -0.29389277, 1), (-0.15450855, -0.47552824, 1), (0.15450856, -0.47552824, 1), (0.40450847, -0.29389265, 1), (0.46193975, 0, 1.1913418), (0.3737171, 0.27152136, 1.1913418), (0.14274722, 0.43933082, 1.1913418), (-0.14274731, 0.4393308, 1.1913418), (-0.37371713, 0.27152133, 1.1913418), (-0.46193975, -4.0384055e-8, 1.1913418), (-0.373717, -0.2715215, 1.1913418), (-0.14274728, -0.4393308, 1.1913418), (0.1427473, -0.4393308, 1.1913418), (0.3737171, -0.2715214, 1.1913418), (0.35355338, 0, 1.3535534), (0.2860307, 0.20781346, 1.3535534), (0.109253995, 0.33624926, 1.3535534), (-0.10925406, 0.33624923, 1.3535534), (-0.2860307, 0.20781344, 1.3535534), (-0.35355338, -3.090862e-8, 1.3535534), (-0.28603062, -0.20781356, 1.3535534), (-0.10925404, -0.33624923, 1.3535534), (0.10925405, -0.33624923, 1.3535534), (0.28603068, -0.20781349, 1.3535534), (0.19134171, 0, 1.4619398), (0.1547987, 0.11246783, 1.4619398), (0.059127837, 0.18197678, 1.4619398), (-0.05912787, 0.18197678, 1.4619398), (-0.15479872, 0.112467825, 1.4619398), (-0.19134171, -1.6727624e-8, 1.4619398), (-0.15479866, -0.11246789, 1.4619398), (-0.05912786, -0.18197678, 1.4619398), (0.059127867, -0.18197678, 1.4619398), (0.15479869, -0.11246785, 1.4619398), (0, 0, 1.5)] + + + +2. Cone +------- +radius = 0.5, height = 2, sweep = 360 + +Topology: + (rightHanded, catmullClark, ([3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4]), ([2, 1, 0, 3, 2, 0, 4, 3, 0, 5, 4, 0, 6, 5, 0, 7, 6, 0, 8, 7, 0, 9, 8, 0, 10, 9, 0, 1, 10, 0, 11, 12, 22, 21, 12, 13, 23, 22, 13, 14, 24, 23, 14, 15, 25, 24, 15, 16, 26, 25, 16, 17, 27, 26, 17, 18, 28, 27, 18, 19, 29, 28, 19, 20, 30, 29, 20, 11, 21, 30]), ([])) + +Points: + [(0, 0, -1), (0.5, 0, -1), (0.4045085, 0.29389262, -1), (0.15450849, 0.47552827, -1), (-0.15450858, 0.47552824, -1), (-0.40450853, 0.2938926, -1), (-0.5, -4.371139e-8, -1), (-0.40450838, -0.29389277, -1), (-0.15450855, -0.47552824, -1), (0.15450856, -0.47552824, -1), (0.40450847, -0.29389265, -1), (0.5, 0, -1), (0.4045085, 0.29389262, -1), (0.15450849, 0.47552827, -1), (-0.15450858, 0.47552824, -1), (-0.40450853, 0.2938926, -1), (-0.5, -4.371139e-8, -1), (-0.40450838, -0.29389277, -1), (-0.15450855, -0.47552824, -1), (0.15450856, -0.47552824, -1), (0.40450847, -0.29389265, -1), (0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1)] + + + +3. Cube +------- +side = 1 + +Topology: + (rightHanded, bilinear, ([4, 4, 4, 4, 4, 4]), ([0, 1, 2, 3, 4, 5, 6, 7, 0, 6, 5, 1, 4, 7, 3, 2, 0, 3, 7, 6, 4, 2, 1, 5]), ([])) + +Points: + [(0.5, 0.5, 0.5), (-0.5, 0.5, 0.5), (-0.5, -0.5, 0.5), (0.5, -0.5, 0.5), (-0.5, -0.5, -0.5), (-0.5, 0.5, -0.5), (0.5, 0.5, -0.5), (0.5, -0.5, -0.5)] + + + +4. Cylinder +----------- +radius = 0.5, height = 2, sweep = 360 + +Topology: + (rightHanded, catmullClark, ([3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3]), ([2, 1, 0, 3, 2, 0, 4, 3, 0, 5, 4, 0, 6, 5, 0, 7, 6, 0, 8, 7, 0, 9, 8, 0, 10, 9, 0, 1, 10, 0, 11, 12, 22, 21, 12, 13, 23, 22, 13, 14, 24, 23, 14, 15, 25, 24, 15, 16, 26, 25, 16, 17, 27, 26, 17, 18, 28, 27, 18, 19, 29, 28, 19, 20, 30, 29, 20, 11, 21, 30, 31, 32, 41, 32, 33, 41, 33, 34, 41, 34, 35, 41, 35, 36, 41, 36, 37, 41, 37, 38, 41, 38, 39, 41, 39, 40, 41, 40, 31, 41]), ([])) + +Points: + [(0, 0, -1), (0.5, 0, -1), (0.4045085, 0.29389262, -1), (0.15450849, 0.47552827, -1), (-0.15450858, 0.47552824, -1), (-0.40450853, 0.2938926, -1), (-0.5, -4.371139e-8, -1), (-0.40450838, -0.29389277, -1), (-0.15450855, -0.47552824, -1), (0.15450856, -0.47552824, -1), (0.40450847, -0.29389265, -1), (0.5, 0, -1), (0.4045085, 0.29389262, -1), (0.15450849, 0.47552827, -1), (-0.15450858, 0.47552824, -1), (-0.40450853, 0.2938926, -1), (-0.5, -4.371139e-8, -1), (-0.40450838, -0.29389277, -1), (-0.15450855, -0.47552824, -1), (0.15450856, -0.47552824, -1), (0.40450847, -0.29389265, -1), (0.5, 0, 1), (0.4045085, 0.29389262, 1), (0.15450849, 0.47552827, 1), (-0.15450858, 0.47552824, 1), (-0.40450853, 0.2938926, 1), (-0.5, -4.371139e-8, 1), (-0.40450838, -0.29389277, 1), (-0.15450855, -0.47552824, 1), (0.15450856, -0.47552824, 1), (0.40450847, -0.29389265, 1), (0.5, 0, 1), (0.4045085, 0.29389262, 1), (0.15450849, 0.47552827, 1), (-0.15450858, 0.47552824, 1), (-0.40450853, 0.2938926, 1), (-0.5, -4.371139e-8, 1), (-0.40450838, -0.29389277, 1), (-0.15450855, -0.47552824, 1), (0.15450856, -0.47552824, 1), (0.40450847, -0.29389265, 1), (0, 0, 1)] + + + +5. Sphere +--------- +radius = 0.5, sweep = 360 + +Topology: + (rightHanded, catmullClark, ([3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3]), ([2, 1, 0, 3, 2, 0, 4, 3, 0, 5, 4, 0, 6, 5, 0, 7, 6, 0, 8, 7, 0, 9, 8, 0, 10, 9, 0, 1, 10, 0, 1, 2, 12, 11, 2, 3, 13, 12, 3, 4, 14, 13, 4, 5, 15, 14, 5, 6, 16, 15, 6, 7, 17, 16, 7, 8, 18, 17, 8, 9, 19, 18, 9, 10, 20, 19, 10, 1, 11, 20, 11, 12, 22, 21, 12, 13, 23, 22, 13, 14, 24, 23, 14, 15, 25, 24, 15, 16, 26, 25, 16, 17, 27, 26, 17, 18, 28, 27, 18, 19, 29, 28, 19, 20, 30, 29, 20, 11, 21, 30, 21, 22, 32, 31, 22, 23, 33, 32, 23, 24, 34, 33, 24, 25, 35, 34, 25, 26, 36, 35, 26, 27, 37, 36, 27, 28, 38, 37, 28, 29, 39, 38, 29, 30, 40, 39, 30, 21, 31, 40, 31, 32, 42, 41, 32, 33, 43, 42, 33, 34, 44, 43, 34, 35, 45, 44, 35, 36, 46, 45, 36, 37, 47, 46, 37, 38, 48, 47, 38, 39, 49, 48, 39, 40, 50, 49, 40, 31, 41, 50, 41, 42, 52, 51, 42, 43, 53, 52, 43, 44, 54, 53, 44, 45, 55, 54, 45, 46, 56, 55, 46, 47, 57, 56, 47, 48, 58, 57, 48, 49, 59, 58, 49, 50, 60, 59, 50, 41, 51, 60, 51, 52, 62, 61, 52, 53, 63, 62, 53, 54, 64, 63, 54, 55, 65, 64, 55, 56, 66, 65, 56, 57, 67, 66, 57, 58, 68, 67, 58, 59, 69, 68, 59, 60, 70, 69, 60, 51, 61, 70, 61, 62, 72, 71, 62, 63, 73, 72, 63, 64, 74, 73, 64, 65, 75, 74, 65, 66, 76, 75, 66, 67, 77, 76, 67, 68, 78, 77, 68, 69, 79, 78, 69, 70, 80, 79, 70, 61, 71, 80, 71, 72, 82, 81, 72, 73, 83, 82, 73, 74, 84, 83, 74, 75, 85, 84, 75, 76, 86, 85, 76, 77, 87, 86, 77, 78, 88, 87, 78, 79, 89, 88, 79, 80, 90, 89, 80, 71, 81, 90, 81, 82, 91, 82, 83, 91, 83, 84, 91, 84, 85, 91, 85, 86, 91, 86, 87, 91, 87, 88, 91, 88, 89, 91, 89, 90, 91, 90, 81, 91]), ([])) + +Points: + [(0, 0, -0.5), (0.15450849, 0, -0.47552827), (0.12499999, 0.09081781, -0.47552827), (0.047745746, 0.14694631, -0.47552827), (-0.04774577, 0.1469463, -0.47552827), (-0.125, 0.0908178, -0.47552827), (-0.15450849, -1.3507561e-8, -0.47552827), (-0.124999955, -0.090817854, -0.47552827), (-0.047745764, -0.1469463, -0.47552827), (0.047745768, -0.1469463, -0.47552827), (0.124999985, -0.09081782, -0.47552827), (0.29389265, 0, -0.4045085), (0.23776415, 0.17274576, -0.4045085), (0.09081782, 0.27950853, -0.4045085), (-0.09081787, 0.2795085, -0.4045085), (-0.23776416, 0.17274575, -0.4045085), (-0.29389265, -2.5692911e-8, -0.4045085), (-0.23776408, -0.17274585, -0.4045085), (-0.090817854, -0.2795085, -0.4045085), (0.09081786, -0.2795085, -0.4045085), (0.23776414, -0.17274578, -0.4045085), (0.4045085, 0, -0.29389262), (0.32725427, 0.23776414, -0.29389262), (0.12499999, 0.38471046, -0.29389262), (-0.12500006, 0.38471043, -0.29389262), (-0.32725427, 0.2377641, -0.29389262), (-0.4045085, -3.5363257e-8, -0.29389262), (-0.32725415, -0.23776425, -0.29389262), (-0.12500004, -0.38471043, -0.29389262), (0.12500006, -0.38471043, -0.29389262), (0.32725424, -0.23776415, -0.29389262), (0.47552827, 0, -0.15450849), (0.38471046, 0.2795085, -0.15450849), (0.14694631, 0.45225427, -0.15450849), (-0.14694639, 0.45225424, -0.15450849), (-0.3847105, 0.27950847, -0.15450849), (-0.47552827, -4.1572e-8, -0.15450849), (-0.38471034, -0.27950865, -0.15450849), (-0.14694637, -0.45225424, -0.15450849), (0.14694637, -0.45225424, -0.15450849), (0.38471043, -0.27950853, -0.15450849), (0.5, 0, 0), (0.4045085, 0.29389262, 0), (0.15450849, 0.47552827, 0), (-0.15450858, 0.47552824, 0), (-0.40450853, 0.2938926, 0), (-0.5, -4.371139e-8, 0), (-0.40450838, -0.29389277, 0), (-0.15450855, -0.47552824, 0), (0.15450856, -0.47552824, 0), (0.40450847, -0.29389265, 0), (0.47552824, 0, 0.15450853), (0.38471043, 0.27950847, 0.15450853), (0.1469463, 0.45225424, 0.15450853), (-0.14694639, 0.4522542, 0.15450853), (-0.38471046, 0.27950844, 0.15450853), (-0.47552824, -4.1571997e-8, 0.15450853), (-0.3847103, -0.27950862, 0.15450853), (-0.14694636, -0.4522542, 0.15450853), (0.14694637, -0.4522542, 0.15450853), (0.3847104, -0.2795085, 0.15450853), (0.4045085, 0, 0.29389262), (0.32725427, 0.23776414, 0.29389262), (0.12499999, 0.38471046, 0.29389262), (-0.12500006, 0.38471043, 0.29389262), (-0.32725427, 0.2377641, 0.29389262), (-0.4045085, -3.5363257e-8, 0.29389262), (-0.32725415, -0.23776425, 0.29389262), (-0.12500004, -0.38471043, 0.29389262), (0.12500006, -0.38471043, 0.29389262), (0.32725424, -0.23776415, 0.29389262), (0.29389262, 0, 0.4045085), (0.23776414, 0.17274575, 0.4045085), (0.09081781, 0.2795085, 0.4045085), (-0.09081786, 0.27950847, 0.4045085), (-0.23776415, 0.17274573, 0.4045085), (-0.29389262, -2.569291e-8, 0.4045085), (-0.23776406, -0.17274584, 0.4045085), (-0.09081785, -0.27950847, 0.4045085), (0.090817854, -0.27950847, 0.4045085), (0.2377641, -0.17274576, 0.4045085), (0.15450853, 0, 0.47552824), (0.12500003, 0.09081783, 0.47552824), (0.047745757, 0.14694636, 0.47552824), (-0.047745787, 0.14694634, 0.47552824), (-0.12500004, 0.090817824, 0.47552824), (-0.15450853, -1.3507565e-8, 0.47552824), (-0.12499999, -0.09081788, 0.47552824), (-0.047745775, -0.14694634, 0.47552824), (0.047745783, -0.14694634, 0.47552824), (0.12500001, -0.09081785, 0.47552824), (0, 0, 0.5)] + + + diff --git a/pxr/imaging/geomUtil/testenv/testMeshGenerators/baseline/generatedMeshes_open.txt b/pxr/imaging/geomUtil/testenv/testMeshGenerators/baseline/generatedMeshes_open.txt new file mode 100644 index 0000000000..ee96ae0962 --- /dev/null +++ b/pxr/imaging/geomUtil/testenv/testMeshGenerators/baseline/generatedMeshes_open.txt @@ -0,0 +1,60 @@ +1. Capsule +---------- +radius = 0.5, height = 2, sweep = 120 + +Topology: + (rightHanded, catmullClark, ([3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3]), ([2, 1, 0, 3, 2, 0, 4, 3, 0, 5, 4, 0, 6, 5, 0, 7, 6, 0, 8, 7, 0, 9, 8, 0, 10, 9, 0, 11, 10, 0, 1, 2, 13, 12, 2, 3, 14, 13, 3, 4, 15, 14, 4, 5, 16, 15, 5, 6, 17, 16, 6, 7, 18, 17, 7, 8, 19, 18, 8, 9, 20, 19, 9, 10, 21, 20, 10, 11, 22, 21, 12, 13, 24, 23, 13, 14, 25, 24, 14, 15, 26, 25, 15, 16, 27, 26, 16, 17, 28, 27, 17, 18, 29, 28, 18, 19, 30, 29, 19, 20, 31, 30, 20, 21, 32, 31, 21, 22, 33, 32, 23, 24, 35, 34, 24, 25, 36, 35, 25, 26, 37, 36, 26, 27, 38, 37, 27, 28, 39, 38, 28, 29, 40, 39, 29, 30, 41, 40, 30, 31, 42, 41, 31, 32, 43, 42, 32, 33, 44, 43, 34, 35, 46, 45, 35, 36, 47, 46, 36, 37, 48, 47, 37, 38, 49, 48, 38, 39, 50, 49, 39, 40, 51, 50, 40, 41, 52, 51, 41, 42, 53, 52, 42, 43, 54, 53, 43, 44, 55, 54, 45, 46, 57, 56, 46, 47, 58, 57, 47, 48, 59, 58, 48, 49, 60, 59, 49, 50, 61, 60, 50, 51, 62, 61, 51, 52, 63, 62, 52, 53, 64, 63, 53, 54, 65, 64, 54, 55, 66, 65, 56, 57, 68, 67, 57, 58, 69, 68, 58, 59, 70, 69, 59, 60, 71, 70, 60, 61, 72, 71, 61, 62, 73, 72, 62, 63, 74, 73, 63, 64, 75, 74, 64, 65, 76, 75, 65, 66, 77, 76, 67, 68, 79, 78, 68, 69, 80, 79, 69, 70, 81, 80, 70, 71, 82, 81, 71, 72, 83, 82, 72, 73, 84, 83, 73, 74, 85, 84, 74, 75, 86, 85, 75, 76, 87, 86, 76, 77, 88, 87, 78, 79, 89, 79, 80, 89, 80, 81, 89, 81, 82, 89, 82, 83, 89, 83, 84, 89, 84, 85, 89, 85, 86, 89, 86, 87, 89, 87, 88, 89]), ([])) + +Points: + [(0, 0, -1.5), (0.19134171, 0, -1.4619398), (0.18716045, 0.03978218, -1.4619398), (0.17479935, 0.07782569, -1.4619398), (0.1547987, 0.11246783, -1.4619398), (0.1280326, 0.14219461, -1.4619398), (0.09567085, 0.1657068, -1.4619398), (0.059127837, 0.18197678, -1.4619398), (0.020000648, 0.19029352, -1.4619398), (-0.020000665, 0.19029352, -1.4619398), (-0.05912785, 0.18197678, -1.4619398), (-0.09567087, 0.16570678, -1.4619398), (0.35355338, 0, -1.3535534), (0.3458274, 0.07350788, -1.3535534), (0.32298708, 0.14380312, -1.3535534), (0.2860307, 0.20781346, -1.3535534), (0.23657337, 0.2627414, -1.3535534), (0.17677668, 0.30618623, -1.3535534), (0.109253995, 0.33624926, -1.3535534), (0.036956377, 0.3516166, -1.3535534), (-0.036956407, 0.3516166, -1.3535534), (-0.10925402, 0.33624923, -1.3535534), (-0.1767767, 0.3061862, -1.3535534), (0.46193975, 0, -1.1913418), (0.45184526, 0.09604268, -1.1913418), (0.42200294, 0.18788782, -1.1913418), (0.3737171, 0.27152136, -1.1913418), (0.309098, 0.34328815, -1.1913418), (0.23096986, 0.4000516, -1.1913418), (0.14274722, 0.43933082, -1.1913418), (0.04828583, 0.4594092, -1.1913418), (-0.04828587, 0.4594092, -1.1913418), (-0.14274725, 0.4393308, -1.1913418), (-0.2309699, 0.40005156, -1.1913418), (0.5, 0, -1), (0.4890738, 0.10395585, -1), (0.4567727, 0.20336832, -1), (0.4045085, 0.29389262, -1), (0.33456528, 0.37157243, -1), (0.24999999, 0.43301272, -1), (0.15450849, 0.47552827, -1), (0.05226421, 0.49726096, -1), (-0.052264255, 0.49726096, -1), (-0.15450852, 0.47552824, -1), (-0.25000003, 0.4330127, -1), (0.5, 0, 1), (0.4890738, 0.10395585, 1), (0.4567727, 0.20336832, 1), (0.4045085, 0.29389262, 1), (0.33456528, 0.37157243, 1), (0.24999999, 0.43301272, 1), (0.15450849, 0.47552827, 1), (0.05226421, 0.49726096, 1), (-0.052264255, 0.49726096, 1), (-0.15450852, 0.47552824, 1), (-0.25000003, 0.4330127, 1), (0.46193975, 0, 1.1913418), (0.45184526, 0.09604268, 1.1913418), (0.42200294, 0.18788782, 1.1913418), (0.3737171, 0.27152136, 1.1913418), (0.309098, 0.34328815, 1.1913418), (0.23096986, 0.4000516, 1.1913418), (0.14274722, 0.43933082, 1.1913418), (0.04828583, 0.4594092, 1.1913418), (-0.04828587, 0.4594092, 1.1913418), (-0.14274725, 0.4393308, 1.1913418), (-0.2309699, 0.40005156, 1.1913418), (0.35355338, 0, 1.3535534), (0.3458274, 0.07350788, 1.3535534), (0.32298708, 0.14380312, 1.3535534), (0.2860307, 0.20781346, 1.3535534), (0.23657337, 0.2627414, 1.3535534), (0.17677668, 0.30618623, 1.3535534), (0.109253995, 0.33624926, 1.3535534), (0.036956377, 0.3516166, 1.3535534), (-0.036956407, 0.3516166, 1.3535534), (-0.10925402, 0.33624923, 1.3535534), (-0.1767767, 0.3061862, 1.3535534), (0.19134171, 0, 1.4619398), (0.18716045, 0.03978218, 1.4619398), (0.17479935, 0.07782569, 1.4619398), (0.1547987, 0.11246783, 1.4619398), (0.1280326, 0.14219461, 1.4619398), (0.09567085, 0.1657068, 1.4619398), (0.059127837, 0.18197678, 1.4619398), (0.020000648, 0.19029352, 1.4619398), (-0.020000665, 0.19029352, 1.4619398), (-0.05912785, 0.18197678, 1.4619398), (-0.09567087, 0.16570678, 1.4619398), (0, 0, 1.5)] + + + +2. Cone +------- +radius = 0.5, height = 2, sweep = 120 + +Topology: + (rightHanded, catmullClark, ([3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4]), ([2, 1, 0, 3, 2, 0, 4, 3, 0, 5, 4, 0, 6, 5, 0, 7, 6, 0, 8, 7, 0, 9, 8, 0, 10, 9, 0, 11, 10, 0, 12, 13, 24, 23, 13, 14, 25, 24, 14, 15, 26, 25, 15, 16, 27, 26, 16, 17, 28, 27, 17, 18, 29, 28, 18, 19, 30, 29, 19, 20, 31, 30, 20, 21, 32, 31, 21, 22, 33, 32]), ([])) + +Points: + [(0, 0, -1), (0.5, 0, -1), (0.4890738, 0.10395585, -1), (0.4567727, 0.20336832, -1), (0.4045085, 0.29389262, -1), (0.33456528, 0.37157243, -1), (0.24999999, 0.43301272, -1), (0.15450849, 0.47552827, -1), (0.05226421, 0.49726096, -1), (-0.052264255, 0.49726096, -1), (-0.15450852, 0.47552824, -1), (-0.25000003, 0.4330127, -1), (0.5, 0, -1), (0.4890738, 0.10395585, -1), (0.4567727, 0.20336832, -1), (0.4045085, 0.29389262, -1), (0.33456528, 0.37157243, -1), (0.24999999, 0.43301272, -1), (0.15450849, 0.47552827, -1), (0.05226421, 0.49726096, -1), (-0.052264255, 0.49726096, -1), (-0.15450852, 0.47552824, -1), (-0.25000003, 0.4330127, -1), (0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1)] + + + +3. Cube +------- +side = 1 + +Topology: + (rightHanded, bilinear, ([4, 4, 4, 4, 4, 4]), ([0, 1, 2, 3, 4, 5, 6, 7, 0, 6, 5, 1, 4, 7, 3, 2, 0, 3, 7, 6, 4, 2, 1, 5]), ([])) + +Points: + [(0.5, 0.5, 0.5), (-0.5, 0.5, 0.5), (-0.5, -0.5, 0.5), (0.5, -0.5, 0.5), (-0.5, -0.5, -0.5), (-0.5, 0.5, -0.5), (0.5, 0.5, -0.5), (0.5, -0.5, -0.5)] + + + +4. Cylinder +----------- +radius = 0.5, height = 2, sweep = 120 + +Topology: + (rightHanded, catmullClark, ([3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3]), ([2, 1, 0, 3, 2, 0, 4, 3, 0, 5, 4, 0, 6, 5, 0, 7, 6, 0, 8, 7, 0, 9, 8, 0, 10, 9, 0, 11, 10, 0, 12, 13, 24, 23, 13, 14, 25, 24, 14, 15, 26, 25, 15, 16, 27, 26, 16, 17, 28, 27, 17, 18, 29, 28, 18, 19, 30, 29, 19, 20, 31, 30, 20, 21, 32, 31, 21, 22, 33, 32, 34, 35, 45, 35, 36, 45, 36, 37, 45, 37, 38, 45, 38, 39, 45, 39, 40, 45, 40, 41, 45, 41, 42, 45, 42, 43, 45, 43, 44, 45]), ([])) + +Points: + [(0, 0, -1), (0.5, 0, -1), (0.4890738, 0.10395585, -1), (0.4567727, 0.20336832, -1), (0.4045085, 0.29389262, -1), (0.33456528, 0.37157243, -1), (0.24999999, 0.43301272, -1), (0.15450849, 0.47552827, -1), (0.05226421, 0.49726096, -1), (-0.052264255, 0.49726096, -1), (-0.15450852, 0.47552824, -1), (-0.25000003, 0.4330127, -1), (0.5, 0, -1), (0.4890738, 0.10395585, -1), (0.4567727, 0.20336832, -1), (0.4045085, 0.29389262, -1), (0.33456528, 0.37157243, -1), (0.24999999, 0.43301272, -1), (0.15450849, 0.47552827, -1), (0.05226421, 0.49726096, -1), (-0.052264255, 0.49726096, -1), (-0.15450852, 0.47552824, -1), (-0.25000003, 0.4330127, -1), (0.5, 0, 1), (0.4890738, 0.10395585, 1), (0.4567727, 0.20336832, 1), (0.4045085, 0.29389262, 1), (0.33456528, 0.37157243, 1), (0.24999999, 0.43301272, 1), (0.15450849, 0.47552827, 1), (0.05226421, 0.49726096, 1), (-0.052264255, 0.49726096, 1), (-0.15450852, 0.47552824, 1), (-0.25000003, 0.4330127, 1), (0.5, 0, 1), (0.4890738, 0.10395585, 1), (0.4567727, 0.20336832, 1), (0.4045085, 0.29389262, 1), (0.33456528, 0.37157243, 1), (0.24999999, 0.43301272, 1), (0.15450849, 0.47552827, 1), (0.05226421, 0.49726096, 1), (-0.052264255, 0.49726096, 1), (-0.15450852, 0.47552824, 1), (-0.25000003, 0.4330127, 1), (0, 0, 1)] + + + +5. Sphere +--------- +radius = 0.5, sweep = 120 + +Topology: + (rightHanded, catmullClark, ([3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3]), ([2, 1, 0, 3, 2, 0, 4, 3, 0, 5, 4, 0, 6, 5, 0, 7, 6, 0, 8, 7, 0, 9, 8, 0, 10, 9, 0, 11, 10, 0, 1, 2, 13, 12, 2, 3, 14, 13, 3, 4, 15, 14, 4, 5, 16, 15, 5, 6, 17, 16, 6, 7, 18, 17, 7, 8, 19, 18, 8, 9, 20, 19, 9, 10, 21, 20, 10, 11, 22, 21, 12, 13, 24, 23, 13, 14, 25, 24, 14, 15, 26, 25, 15, 16, 27, 26, 16, 17, 28, 27, 17, 18, 29, 28, 18, 19, 30, 29, 19, 20, 31, 30, 20, 21, 32, 31, 21, 22, 33, 32, 23, 24, 35, 34, 24, 25, 36, 35, 25, 26, 37, 36, 26, 27, 38, 37, 27, 28, 39, 38, 28, 29, 40, 39, 29, 30, 41, 40, 30, 31, 42, 41, 31, 32, 43, 42, 32, 33, 44, 43, 34, 35, 46, 45, 35, 36, 47, 46, 36, 37, 48, 47, 37, 38, 49, 48, 38, 39, 50, 49, 39, 40, 51, 50, 40, 41, 52, 51, 41, 42, 53, 52, 42, 43, 54, 53, 43, 44, 55, 54, 45, 46, 57, 56, 46, 47, 58, 57, 47, 48, 59, 58, 48, 49, 60, 59, 49, 50, 61, 60, 50, 51, 62, 61, 51, 52, 63, 62, 52, 53, 64, 63, 53, 54, 65, 64, 54, 55, 66, 65, 56, 57, 68, 67, 57, 58, 69, 68, 58, 59, 70, 69, 59, 60, 71, 70, 60, 61, 72, 71, 61, 62, 73, 72, 62, 63, 74, 73, 63, 64, 75, 74, 64, 65, 76, 75, 65, 66, 77, 76, 67, 68, 79, 78, 68, 69, 80, 79, 69, 70, 81, 80, 70, 71, 82, 81, 71, 72, 83, 82, 72, 73, 84, 83, 73, 74, 85, 84, 74, 75, 86, 85, 75, 76, 87, 86, 76, 77, 88, 87, 78, 79, 90, 89, 79, 80, 91, 90, 80, 81, 92, 91, 81, 82, 93, 92, 82, 83, 94, 93, 83, 84, 95, 94, 84, 85, 96, 95, 85, 86, 97, 96, 86, 87, 98, 97, 87, 88, 99, 98, 89, 90, 100, 90, 91, 100, 91, 92, 100, 92, 93, 100, 93, 94, 100, 94, 95, 100, 95, 96, 100, 96, 97, 100, 97, 98, 100, 98, 99, 100]), ([])) + +Points: + [(0, 0, -0.5), (0.15450849, 0, -0.47552827), (0.1511321, 0.03212412, -0.47552827), (0.14115052, 0.06284426, -0.47552827), (0.12499999, 0.09081781, -0.47552827), (0.10338635, 0.11482219, -0.47552827), (0.077254236, 0.13380828, -0.47552827), (0.047745746, 0.14694631, -0.47552827), (0.016150529, 0.15366207, -0.47552827), (-0.016150542, 0.15366207, -0.47552827), (-0.047745753, 0.1469463, -0.47552827), (-0.07725425, 0.13380827, -0.47552827), (0.29389265, 0, -0.4045085), (0.2874704, 0.06110372, -0.4045085), (0.2684843, 0.119536914, -0.4045085), (0.23776415, 0.17274576, -0.4045085), (0.19665256, 0.21840481, -0.4045085), (0.14694631, 0.2545185, -0.4045085), (0.09081782, 0.27950853, -0.4045085), (0.030720135, 0.29228267, -0.4045085), (-0.030720161, 0.29228267, -0.4045085), (-0.09081783, 0.2795085, -0.4045085), (-0.14694634, 0.2545185, -0.4045085), (0.4045085, 0, -0.29389262), (0.39566904, 0.08410205, -0.29389262), (0.3695369, 0.16452843, -0.29389262), (0.32725427, 0.23776414, -0.29389262), (0.270669, 0.30060843, -0.29389262), (0.20225424, 0.35031465, -0.29389262), (0.12499999, 0.38471046, -0.29389262), (0.042282633, 0.40229258, -0.29389262), (-0.04228267, 0.40229258, -0.29389262), (-0.12500001, 0.38471043, -0.29389262), (-0.20225428, 0.35031462, -0.29389262), (0.47552827, 0, -0.15450849), (0.46513686, 0.09886789, -0.15450849), (0.43441668, 0.19341478, -0.15450849), (0.38471046, 0.2795085, -0.15450849), (0.3181905, 0.3533864, -0.15450849), (0.23776412, 0.41181958, -0.15450849), (0.14694631, 0.45225427, -0.15450849), (0.049706217, 0.47292328, -0.15450849), (-0.04970626, 0.47292328, -0.15450849), (-0.14694634, 0.45225424, -0.15450849), (-0.23776416, 0.41181955, -0.15450849), (0.5, 0, 0), (0.4890738, 0.10395585, 0), (0.4567727, 0.20336832, 0), (0.4045085, 0.29389262, 0), (0.33456528, 0.37157243, 0), (0.24999999, 0.43301272, 0), (0.15450849, 0.47552827, 0), (0.05226421, 0.49726096, 0), (-0.052264255, 0.49726096, 0), (-0.15450852, 0.47552824, 0), (-0.25000003, 0.4330127, 0), (0.47552824, 0, 0.15450853), (0.46513683, 0.098867886, 0.15450853), (0.43441665, 0.19341476, 0.15450853), (0.38471043, 0.27950847, 0.15450853), (0.3181905, 0.35338637, 0.15450853), (0.2377641, 0.41181955, 0.15450853), (0.1469463, 0.45225424, 0.15450853), (0.049706217, 0.47292325, 0.15450853), (-0.049706258, 0.47292325, 0.15450853), (-0.14694633, 0.4522542, 0.15450853), (-0.23776415, 0.41181952, 0.15450853), (0.4045085, 0, 0.29389262), (0.39566904, 0.08410205, 0.29389262), (0.3695369, 0.16452843, 0.29389262), (0.32725427, 0.23776414, 0.29389262), (0.270669, 0.30060843, 0.29389262), (0.20225424, 0.35031465, 0.29389262), (0.12499999, 0.38471046, 0.29389262), (0.042282633, 0.40229258, 0.29389262), (-0.04228267, 0.40229258, 0.29389262), (-0.12500001, 0.38471043, 0.29389262), (-0.20225428, 0.35031462, 0.29389262), (0.29389262, 0, 0.4045085), (0.28747037, 0.061103716, 0.4045085), (0.26848426, 0.1195369, 0.4045085), (0.23776414, 0.17274575, 0.4045085), (0.19665253, 0.2184048, 0.4045085), (0.1469463, 0.25451848, 0.4045085), (0.09081781, 0.2795085, 0.4045085), (0.030720131, 0.29228264, 0.4045085), (-0.030720158, 0.29228264, 0.4045085), (-0.090817824, 0.27950847, 0.4045085), (-0.14694633, 0.25451848, 0.4045085), (0.15450853, 0, 0.47552824), (0.15113215, 0.032124132, 0.47552824), (0.14115056, 0.062844284, 0.47552824), (0.12500003, 0.09081783, 0.47552824), (0.10338638, 0.114822224, 0.47552824), (0.07725426, 0.13380831, 0.47552824), (0.047745757, 0.14694636, 0.47552824), (0.016150532, 0.15366212, 0.47552824), (-0.016150547, 0.15366212, 0.47552824), (-0.047745768, 0.14694634, 0.47552824), (-0.07725427, 0.13380831, 0.47552824), (0, 0, 0.5)] + + + diff --git a/pxr/imaging/geomUtil/wrapCapsuleMeshGenerator.cpp b/pxr/imaging/geomUtil/wrapCapsuleMeshGenerator.cpp new file mode 100644 index 0000000000..a7c6c218eb --- /dev/null +++ b/pxr/imaging/geomUtil/wrapCapsuleMeshGenerator.cpp @@ -0,0 +1,79 @@ +// +// Copyright 2022 Pixar +// +// Licensed under the Apache License, Version 2.0 (the "Apache License") +// with the following modification; you may not use this file except in +// compliance with the Apache License and the following modification to it: +// Section 6. Trademarks. is deleted and replaced with: +// +// 6. Trademarks. This License does not grant permission to use the trade +// names, trademarks, service marks, or product names of the Licensor +// and its affiliates, except as required to comply with Section 4(c) of +// the License and to reproduce the content of the NOTICE file. +// +// You may obtain a copy of the Apache License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the Apache License with the above modification is +// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the Apache License for the specific +// language governing permissions and limitations under the Apache License. +// +#include "pxr/imaging/geomUtil/capsuleMeshGenerator.h" + +#include "pxr/imaging/pxOsd/meshTopology.h" + +#include "pxr/base/vt/types.h" + +#include + +using namespace boost::python; + +PXR_NAMESPACE_USING_DIRECTIVE + +static VtVec3fArray +_WrapGeneratePoints( + const size_t numRadial, + const size_t numCapAxial, + const float radius, + const float height) +{ + const size_t numPoints = + GeomUtilCapsuleMeshGenerator::ComputeNumPoints(numRadial, numCapAxial); + if (numPoints == 0) { + return VtVec3fArray(); + } + + VtVec3fArray points(numPoints); + GeomUtilCapsuleMeshGenerator::GeneratePoints( + points.begin(), numRadial, numCapAxial, radius, height); + + return points; +} + +void wrapCapsuleMeshGenerator() +{ + using This = GeomUtilCapsuleMeshGenerator; + + // Pull the constexpr values into variables so boost can odr-use them. + static constexpr size_t minNumRadial = This::minNumRadial; + static constexpr size_t minNumCapAxial = This::minNumCapAxial; + + // Note: These are only "classes" for name scoping, and are uninstantiable; + // hence no need to bother declaring bases. + class_("CapsuleMeshGenerator", no_init) + .def_readonly("minNumRadial", minNumRadial) + .def_readonly("minNumCapAxial", minNumCapAxial) + + .def("ComputeNumPoints", &This::ComputeNumPoints) + .staticmethod("ComputeNumPoints") + + .def("GenerateTopology", &This::GenerateTopology) + .staticmethod("GenerateTopology") + + .def("GeneratePoints", &_WrapGeneratePoints) + .staticmethod("GeneratePoints") + ; +} \ No newline at end of file diff --git a/pxr/imaging/geomUtil/wrapConeMeshGenerator.cpp b/pxr/imaging/geomUtil/wrapConeMeshGenerator.cpp new file mode 100644 index 0000000000..94e041b9da --- /dev/null +++ b/pxr/imaging/geomUtil/wrapConeMeshGenerator.cpp @@ -0,0 +1,76 @@ +// +// Copyright 2022 Pixar +// +// Licensed under the Apache License, Version 2.0 (the "Apache License") +// with the following modification; you may not use this file except in +// compliance with the Apache License and the following modification to it: +// Section 6. Trademarks. is deleted and replaced with: +// +// 6. Trademarks. This License does not grant permission to use the trade +// names, trademarks, service marks, or product names of the Licensor +// and its affiliates, except as required to comply with Section 4(c) of +// the License and to reproduce the content of the NOTICE file. +// +// You may obtain a copy of the Apache License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the Apache License with the above modification is +// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the Apache License for the specific +// language governing permissions and limitations under the Apache License. +// +#include "pxr/imaging/geomUtil/coneMeshGenerator.h" + +#include "pxr/imaging/pxOsd/meshTopology.h" + +#include "pxr/base/vt/types.h" + +#include + +using namespace boost::python; + +PXR_NAMESPACE_USING_DIRECTIVE + +static VtVec3fArray +_WrapGeneratePoints( + const size_t numRadial, + const float radius, + const float height) +{ + const size_t numPoints = + GeomUtilConeMeshGenerator::ComputeNumPoints(numRadial); + if (numPoints == 0) { + return VtVec3fArray(); + } + + VtVec3fArray points(numPoints); + GeomUtilConeMeshGenerator::GeneratePoints( + points.begin(), numRadial, radius, height); + + return points; +} + +void wrapConeMeshGenerator() +{ + using This = GeomUtilConeMeshGenerator; + + // Pull the constexpr values into variables so boost can odr-use them. + static constexpr size_t minNumRadial = This::minNumRadial; + + // Note: These are only "classes" for name scoping, and are uninstantiable; + // hence no need to bother declaring bases. + class_("ConeMeshGenerator", no_init) + .def_readonly("minNumRadial", minNumRadial) + + .def("ComputeNumPoints", &This::ComputeNumPoints) + .staticmethod("ComputeNumPoints") + + .def("GenerateTopology", &This::GenerateTopology) + .staticmethod("GenerateTopology") + + .def("GeneratePoints", &_WrapGeneratePoints) + .staticmethod("GeneratePoints") + ; +} \ No newline at end of file diff --git a/pxr/imaging/geomUtil/wrapCuboidMeshGenerator.cpp b/pxr/imaging/geomUtil/wrapCuboidMeshGenerator.cpp new file mode 100644 index 0000000000..1ec5f9430a --- /dev/null +++ b/pxr/imaging/geomUtil/wrapCuboidMeshGenerator.cpp @@ -0,0 +1,72 @@ +// +// Copyright 2022 Pixar +// +// Licensed under the Apache License, Version 2.0 (the "Apache License") +// with the following modification; you may not use this file except in +// compliance with the Apache License and the following modification to it: +// Section 6. Trademarks. is deleted and replaced with: +// +// 6. Trademarks. This License does not grant permission to use the trade +// names, trademarks, service marks, or product names of the Licensor +// and its affiliates, except as required to comply with Section 4(c) of +// the License and to reproduce the content of the NOTICE file. +// +// You may obtain a copy of the Apache License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the Apache License with the above modification is +// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the Apache License for the specific +// language governing permissions and limitations under the Apache License. +// +#include "pxr/imaging/geomUtil/cuboidMeshGenerator.h" + +#include "pxr/imaging/pxOsd/meshTopology.h" + +#include "pxr/base/vt/types.h" + +#include + +using namespace boost::python; + +PXR_NAMESPACE_USING_DIRECTIVE + +static VtVec3fArray +_WrapGeneratePoints( + const float xLength, + const float yLength, + const float zLength) +{ + const size_t numPoints = + GeomUtilCuboidMeshGenerator::ComputeNumPoints(); + if (numPoints == 0) { + return VtVec3fArray(); + } + + VtVec3fArray points(numPoints); + GeomUtilCuboidMeshGenerator::GeneratePoints( + points.begin(), xLength, yLength, zLength); + + return points; +} + +void wrapCuboidMeshGenerator() +{ + using This = GeomUtilCuboidMeshGenerator; + + // Note: These are only "classes" for name scoping, and are uninstantiable; + // hence no need to bother declaring bases. + class_("CuboidMeshGenerator", no_init) + + .def("ComputeNumPoints", &This::ComputeNumPoints) + .staticmethod("ComputeNumPoints") + + .def("GenerateTopology", &This::GenerateTopology) + .staticmethod("GenerateTopology") + + .def("GeneratePoints", &_WrapGeneratePoints) + .staticmethod("GeneratePoints") + ; +} \ No newline at end of file diff --git a/pxr/imaging/geomUtil/wrapCylinderMeshGenerator.cpp b/pxr/imaging/geomUtil/wrapCylinderMeshGenerator.cpp new file mode 100644 index 0000000000..ed77b14178 --- /dev/null +++ b/pxr/imaging/geomUtil/wrapCylinderMeshGenerator.cpp @@ -0,0 +1,76 @@ +// +// Copyright 2022 Pixar +// +// Licensed under the Apache License, Version 2.0 (the "Apache License") +// with the following modification; you may not use this file except in +// compliance with the Apache License and the following modification to it: +// Section 6. Trademarks. is deleted and replaced with: +// +// 6. Trademarks. This License does not grant permission to use the trade +// names, trademarks, service marks, or product names of the Licensor +// and its affiliates, except as required to comply with Section 4(c) of +// the License and to reproduce the content of the NOTICE file. +// +// You may obtain a copy of the Apache License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the Apache License with the above modification is +// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the Apache License for the specific +// language governing permissions and limitations under the Apache License. +// +#include "pxr/imaging/geomUtil/cylinderMeshGenerator.h" + +#include "pxr/imaging/pxOsd/meshTopology.h" + +#include "pxr/base/vt/types.h" + +#include + +using namespace boost::python; + +PXR_NAMESPACE_USING_DIRECTIVE + +static VtVec3fArray +_WrapGeneratePoints( + const size_t numRadial, + const float radius, + const float height) +{ + const size_t numPoints = + GeomUtilCylinderMeshGenerator::ComputeNumPoints(numRadial); + if (numPoints == 0) { + return VtVec3fArray(); + } + + VtVec3fArray points(numPoints); + GeomUtilCylinderMeshGenerator::GeneratePoints( + points.begin(), numRadial, radius, height); + + return points; +} + +void wrapCylinderMeshGenerator() +{ + using This = GeomUtilCylinderMeshGenerator; + + // Pull the constexpr values into variables so boost can odr-use them. + static constexpr size_t minNumRadial = This::minNumRadial; + + // Note: These are only "classes" for name scoping, and are uninstantiable; + // hence no need to bother declaring bases. + class_("CylinderMeshGenerator", no_init) + .def_readonly("minNumRadial", minNumRadial) + + .def("ComputeNumPoints", &This::ComputeNumPoints) + .staticmethod("ComputeNumPoints") + + .def("GenerateTopology", &This::GenerateTopology) + .staticmethod("GenerateTopology") + + .def("GeneratePoints", &_WrapGeneratePoints) + .staticmethod("GeneratePoints") + ; +} \ No newline at end of file diff --git a/pxr/imaging/geomUtil/wrapSphereMeshGenerator.cpp b/pxr/imaging/geomUtil/wrapSphereMeshGenerator.cpp new file mode 100644 index 0000000000..15c58de4a5 --- /dev/null +++ b/pxr/imaging/geomUtil/wrapSphereMeshGenerator.cpp @@ -0,0 +1,78 @@ +// +// Copyright 2022 Pixar +// +// Licensed under the Apache License, Version 2.0 (the "Apache License") +// with the following modification; you may not use this file except in +// compliance with the Apache License and the following modification to it: +// Section 6. Trademarks. is deleted and replaced with: +// +// 6. Trademarks. This License does not grant permission to use the trade +// names, trademarks, service marks, or product names of the Licensor +// and its affiliates, except as required to comply with Section 4(c) of +// the License and to reproduce the content of the NOTICE file. +// +// You may obtain a copy of the Apache License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the Apache License with the above modification is +// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the Apache License for the specific +// language governing permissions and limitations under the Apache License. +// +#include "pxr/imaging/geomUtil/sphereMeshGenerator.h" + +#include "pxr/imaging/pxOsd/meshTopology.h" + +#include "pxr/base/vt/types.h" + +#include + +using namespace boost::python; + +PXR_NAMESPACE_USING_DIRECTIVE + +static VtVec3fArray +_WrapGeneratePoints( + const size_t numRadial, + const size_t numAxial, + const float radius) +{ + const size_t numPoints = + GeomUtilSphereMeshGenerator::ComputeNumPoints(numRadial, numAxial); + if (numPoints == 0) { + return VtVec3fArray(); + } + + VtVec3fArray points(numPoints); + GeomUtilSphereMeshGenerator::GeneratePoints( + points.begin(), numRadial, numAxial, radius); + + return points; +} + +void wrapSphereMeshGenerator() +{ + using This = GeomUtilSphereMeshGenerator; + + // Pull the constexpr values into variables so boost can odr-use them. + static constexpr size_t minNumRadial = This::minNumRadial; + static constexpr size_t minNumAxial = This::minNumAxial; + + // Note: These are only "classes" for name scoping, and are uninstantiable; + // hence no need to bother declaring bases. + class_("SphereMeshGenerator", no_init) + .def_readonly("minNumRadial", minNumRadial) + .def_readonly("minNumAxial", minNumAxial) + + .def("ComputeNumPoints", &This::ComputeNumPoints) + .staticmethod("ComputeNumPoints") + + .def("GenerateTopology", &This::GenerateTopology) + .staticmethod("GenerateTopology") + + .def("GeneratePoints", &_WrapGeneratePoints) + .staticmethod("GeneratePoints") + ; +} \ No newline at end of file diff --git a/pxr/imaging/hd/CMakeLists.txt b/pxr/imaging/hd/CMakeLists.txt index c33375b911..ea19e6ced3 100644 --- a/pxr/imaging/hd/CMakeLists.txt +++ b/pxr/imaging/hd/CMakeLists.txt @@ -81,6 +81,7 @@ pxr_library(hd geomSubsetSchema geomSubsetsSchema instanceCategoriesSchema + instancedBySceneIndex instancedBySchema instancer instanceRegistry diff --git a/pxr/imaging/hd/basisCurvesTopology.cpp b/pxr/imaging/hd/basisCurvesTopology.cpp index 9a3c824261..85a17496bc 100644 --- a/pxr/imaging/hd/basisCurvesTopology.cpp +++ b/pxr/imaging/hd/basisCurvesTopology.cpp @@ -27,6 +27,8 @@ #include "pxr/base/arch/hash.h" +#include + PXR_NAMESPACE_OPEN_SCOPE namespace { @@ -36,14 +38,13 @@ _ComputeNumPoints( VtIntArray const &curveVertexCounts, VtIntArray const &indices) { + // Make absolutely sure the iterator is constant + // (so we don't detach the array while multi-threaded) if (indices.empty()) { - size_t sum = 0; - for (int i : curveVertexCounts) { - sum += i; - } - return sum; + return std::accumulate( + curveVertexCounts.cbegin(), curveVertexCounts.cend(), size_t {0} ); } else { - return 1 + *std::max_element(indices.begin(), indices.end()); + return 1 + *std::max_element(indices.cbegin(), indices.cend()); } } @@ -166,16 +167,8 @@ operator << (std::ostream &out, HdBasisCurvesTopology const &topo) size_t HdBasisCurvesTopology::CalculateNeededNumberOfControlPoints() const { - size_t numVerts= 0; - - // Make absolutely sure the iterator is constant - // (so we don't detach the array while multi-threaded) - for (VtIntArray::const_iterator itCounts = _curveVertexCounts.cbegin(); - itCounts != _curveVertexCounts.cend(); ++itCounts) { - numVerts += *itCounts; - } - - return numVerts; + // This is computed on construction and accounts for authored indices. + return _numPoints; } size_t @@ -185,7 +178,8 @@ HdBasisCurvesTopology::CalculateNeededNumberOfVaryingControlPoints() const // For linear curves, varying and vertex interpolation is identical. return CalculateNeededNumberOfControlPoints(); } - size_t numVerts= 0; + + size_t numVarying = 0; int numSegs = 0, vStep = 0; bool wrap = GetCurveWrap() == HdTokens->periodic; @@ -201,22 +195,28 @@ HdBasisCurvesTopology::CalculateNeededNumberOfVaryingControlPoints() const for (VtIntArray::const_iterator itCounts = _curveVertexCounts.cbegin(); itCounts != _curveVertexCounts.cend(); ++itCounts) { - // Handling for the case of potentially incorrect vertex counts + // Partial handling for the case of potentially incorrect vertex counts. + // We don't validate the vertex count for each curve (which differs + // based on the basis and wrap mode) since a renderer + // may choose to handle underspecified vertices via e.g., repetition. if (*itCounts < 1) { continue; } - // The number of verts is different if we have periodic vs non-periodic - // curves, check basisCurvesComputations.cpp line 207 for a diagram. + // The number of segments is different if we have periodic vs + // non-periodic curves, check basisCurvesComputations.cpp for a diagram. if (wrap) { - numSegs = *itCounts / vStep; + // For bezier curves, if the authored vertex count is less than the + // minimum, treat it as 1 segment. + numSegs = std::max(*itCounts / vStep, 1); + numVarying += numSegs; } else { - numSegs = ((*itCounts - 4) / vStep) + 1; + numSegs = (std::max(*itCounts - 4, 0) / vStep) + 1; + numVarying += numSegs + 1; } - numVerts += numSegs + 1; } - return numVerts; + return numVarying; } PXR_NAMESPACE_CLOSE_SCOPE diff --git a/pxr/imaging/hd/bufferArrayRegistry.cpp b/pxr/imaging/hd/bufferArrayRegistry.cpp index 8b8f8c782e..fa196977d7 100644 --- a/pxr/imaging/hd/bufferArrayRegistry.cpp +++ b/pxr/imaging/hd/bufferArrayRegistry.cpp @@ -100,6 +100,7 @@ HdBufferArrayRangeSharedPtr HdBufferArrayRegistry::AllocateRange( // on check end condition. _HdBufferArraySharedPtrList::iterator it = entry.bufferArrays.begin(); + int numIterations = 0; do { HdBufferArraySharedPtr currentArray = *it; @@ -121,9 +122,21 @@ HdBufferArrayRangeSharedPtr HdBufferArrayRegistry::AllocateRange( it = prev; ++it; } + } + + if (++numIterations > 100) { + TF_WARN("Too many iterations in attempting to assign range " + "containing buffer %s, likely due to invalid buffer array " + "size.", + bufferSpecs[0].name.GetText()); + break; } } while (!range->IsAssigned()); + if (!range->IsAssigned()) { + return HdBufferArrayRangeSharedPtr(); + } + return range; } diff --git a/pxr/imaging/hd/bufferSpec.cpp b/pxr/imaging/hd/bufferSpec.cpp index ef7d5c5bcb..089944d7d9 100644 --- a/pxr/imaging/hd/bufferSpec.cpp +++ b/pxr/imaging/hd/bufferSpec.cpp @@ -50,17 +50,26 @@ HdBufferSpecVector HdBufferSpec::ComputeUnion(HdBufferSpecVector const &specs1, HdBufferSpecVector const &specs2) { + // This implementation assumes small inputs. + HD_TRACE_FUNCTION(); - std::set set; - for(HdBufferSpec const& spec : specs1) { - set.insert(spec); + HdBufferSpecVector r; + + for (HdBufferSpec const& s : specs1) { + if (std::find(r.begin(), r.end(), s) != r.end()) { + continue; + } + r.push_back(s); } - for(HdBufferSpec const& spec : specs2) { - set.insert(spec); + for (HdBufferSpec const& s : specs2) { + if (std::find(r.begin(), r.end(), s) != r.end()) { + continue; + } + r.push_back(s); } - return HdBufferSpecVector(set.begin(), set.end()); + return r; } /*static*/ @@ -68,17 +77,23 @@ HdBufferSpecVector HdBufferSpec::ComputeDifference(HdBufferSpecVector const &specs1, HdBufferSpecVector const &specs2) { + // This implementation assumes small inputs. + HD_TRACE_FUNCTION(); - std::set set; - for(HdBufferSpec const& spec : specs1) { - set.insert(spec); - } - for(HdBufferSpec const& spec : specs2) { - set.erase(spec); + HdBufferSpecVector r; + + for(HdBufferSpec const& s : specs1) { + if (std::find(specs2.begin(), specs2.end(), s) != specs2.end()) { + continue; + } + if (std::find(r.begin(), r.end(), s) != r.end()) { + continue; + } + r.push_back(s); } - return HdBufferSpecVector(set.begin(), set.end()); + return r; } size_t diff --git a/pxr/imaging/hd/bufferSpec.h b/pxr/imaging/hd/bufferSpec.h index 25b7be8ed6..5df0962090 100644 --- a/pxr/imaging/hd/bufferSpec.h +++ b/pxr/imaging/hd/bufferSpec.h @@ -72,14 +72,17 @@ struct HdBufferSpec final { static bool IsSubset(HdBufferSpecVector const &subset, HdBufferSpecVector const &superset); - /// Returns union set of \p spec1 and \p spec2. - /// Duplicated entries are uniquified. + /// Returns union set of \p spec1 and \p spec2. Duplicated entries are + /// uniquified. The order of items in spec1 and spec2 are preserved relative + /// to themselves respectively in the result, with items in spec1 appearing + /// first. HD_API static HdBufferSpecVector ComputeUnion(HdBufferSpecVector const &spec1, HdBufferSpecVector const &spec2); /// Returns difference set of \p spec1 and \p spec2, i.e., entries in spec1 - /// that are not in spec2. + /// that are not in spec2. Duplicated entries are uniquified. The order of + /// items in spec1 is preserved. HD_API static HdBufferSpecVector ComputeDifference(HdBufferSpecVector const &spec1, HdBufferSpecVector const &spec2); @@ -92,7 +95,7 @@ struct HdBufferSpec final { HD_API size_t Hash() const; - /// Functor to use for unorderd sets, maps. + /// Functor to use for unordered sets, maps. struct HashFunctor { size_t operator()(HdBufferSpec const& spec) const { return spec.Hash(); diff --git a/pxr/imaging/hd/dataSource.h b/pxr/imaging/hd/dataSource.h index 2d93b566ec..0a42bb89a7 100644 --- a/pxr/imaging/hd/dataSource.h +++ b/pxr/imaging/hd/dataSource.h @@ -240,13 +240,17 @@ HdGetMergedContributingSampleTimesForInterval( std::vector * outSampleTimes); /// Print a datasource to a stream, for debugging/testing. -void HdDebugPrintDataSource( +HD_API +void +HdDebugPrintDataSource( std::ostream &, HdDataSourceBaseHandle, int indentLevel = 0); /// Print a datasource to stdout, for debugging/testing -void HdDebugPrintDataSource(HdDataSourceBaseHandle, int indentLevel = 0); +HD_API +void +HdDebugPrintDataSource(HdDataSourceBaseHandle, int indentLevel = 0); PXR_NAMESPACE_CLOSE_SCOPE diff --git a/pxr/imaging/hd/dataSourceLegacyPrim.cpp b/pxr/imaging/hd/dataSourceLegacyPrim.cpp index 41843cc2da..ee4d09e3bf 100644 --- a/pxr/imaging/hd/dataSourceLegacyPrim.cpp +++ b/pxr/imaging/hd/dataSourceLegacyPrim.cpp @@ -1851,12 +1851,9 @@ class Hd_DataSourceLegacyExtComputationPrimvarsContainer HD_DECLARE_DATASOURCE(Hd_DataSourceLegacyExtComputationPrimvarsContainer); Hd_DataSourceLegacyExtComputationPrimvarsContainer( - const SdfPath &primId, - HdSceneDelegate *sceneDelegate) + const SdfPath &primId) : _primId(primId) - , _sceneDelegate(sceneDelegate) { - TF_VERIFY(_sceneDelegate); } void AddDesc(const TfToken &name, const TfToken &interpolation, @@ -1918,7 +1915,6 @@ class Hd_DataSourceLegacyExtComputationPrimvarsContainer _EntryMap _entries; SdfPath _primId; - HdSceneDelegate *_sceneDelegate; }; HD_DECLARE_DATASOURCE_HANDLES( @@ -2638,7 +2634,8 @@ _ConvertHdMaterialNetworkToHdDataSources( cNames.data(), cValues.data()), HdRetainedTypedSampledDataSource::New( - node.identifier))); + node.identifier), + nullptr /*renderContextNodeIdentifiers*/)); } terminalsValues.push_back( @@ -2699,7 +2696,8 @@ _ConvertSampleFilterNodeToHdDataSources( paramsValues.data()), HdRetainedContainerDataSource::New(),// SampleFilter has no connections HdRetainedTypedSampledDataSource::New( - hdNode.nodeTypeId)); + hdNode.nodeTypeId), + nullptr /*renderContextNodeIdentifiers*/); *result = HdSampleFilterSchema::BuildRetained(nodeDS); @@ -2767,7 +2765,7 @@ HdDataSourceLegacyPrim::_GetExtComputationPrimvarsDataSource() if (!primvarsDs) { primvarsDs = Hd_DataSourceLegacyExtComputationPrimvarsContainer::New( - _id, _sceneDelegate); + _id); } primvarsDs->AddDesc( primvarDesc.name, interpolationToken, primvarDesc.role, diff --git a/pxr/imaging/hd/dataSourceLocator.cpp b/pxr/imaging/hd/dataSourceLocator.cpp index 1879c83f67..0a0bf9d0c5 100644 --- a/pxr/imaging/hd/dataSourceLocator.cpp +++ b/pxr/imaging/hd/dataSourceLocator.cpp @@ -23,6 +23,8 @@ // #include "pxr/imaging/hd/dataSourceLocator.h" +#include "pxr/base/arch/hints.h" + #include #include @@ -579,4 +581,100 @@ HdDataSourceLocatorSet::IsEmpty() const return _locators.empty(); } +HdDataSourceLocatorSet +HdDataSourceLocatorSet::ReplacePrefix( + const HdDataSourceLocator &oldPrefix, + const HdDataSourceLocator &newPrefix) const +{ + if (ARCH_UNLIKELY(IsEmpty() || (oldPrefix == newPrefix))) { + return *this; + } + + auto sortAndUniquifyLocators = [](_Locators *pLocators) { + + _Locators &locators = *pLocators; + std::sort(locators.begin(), locators.end()); + + for (size_t i = 1; i < locators.size(); ) { + if (locators[i].Intersects(locators[i-1])) { + locators.erase(locators.begin() + i); + } else { + ++i; + } + } + }; + + // Note: See _binarySearchCutoff in Intersects. + constexpr size_t _binarySearchCutoff = 5; + + if (_locators.size() < _binarySearchCutoff) { + HdDataSourceLocatorSet result = *this; + _Locators &locators = result._locators; + + for (auto &l : locators) { + l = l.ReplacePrefix(oldPrefix, newPrefix); + } + + sortAndUniquifyLocators(&locators); + + return result; + } + + TRACE_FUNCTION(); + // lower_bound with operator < gives us the first element that is not less + // than (i.e., greater than or equal to) oldPrefix, which is what we want + // here (unlike in the insertion case where we use _LessThanNotPrefix). + // e.g. given the locator set {a/a, a/b/c, a/b/d, a/c} and the prefix a/b, + // lower_bound gives us the element a/b/c. + auto it = + std::lower_bound(_locators.begin(), _locators.end(), oldPrefix); + + if (it != _locators.end()) { + + if (it->HasPrefix(oldPrefix)) { + + HdDataSourceLocatorSet result = *this; + _Locators &locators = result._locators; + + auto lowerIt = locators.begin() + + std::distance(_locators.begin(), it); + + if (*lowerIt == oldPrefix) { + // The closed under descendancy nature of HdDataLocatorSet + // implies that the next element cannot be a descendant of the + // current one, implying that it won't share the prefix. + *lowerIt = newPrefix; + + } else { + + // Find first element such that elem.HasPrefix(oldPrefix) is + // false. + auto upperIt = + std::lower_bound( + std::next(lowerIt, 1), locators.end(), oldPrefix, + [](const HdDataSourceLocator &elem, + const HdDataSourceLocator &prefix) { + + return elem.HasPrefix(prefix); + + }); + + for (auto it = lowerIt; it < upperIt; it++) { + *it = it->ReplacePrefix(oldPrefix, newPrefix); + } + } + + // Since we've edited in-place, sort and uniquify the locators. + // XXX We could be smarter in the range that is sorted by factoring + // lower and upper bounds with the new prefix. + sortAndUniquifyLocators(&locators); + return result; + } + // Otherwise, there's nothing to do since no element in the set + // has the prefix oldPrefix. + } + + return *this; +} + PXR_NAMESPACE_CLOSE_SCOPE diff --git a/pxr/imaging/hd/dataSourceLocator.h b/pxr/imaging/hd/dataSourceLocator.h index d3c2081b07..d379ecfb98 100644 --- a/pxr/imaging/hd/dataSourceLocator.h +++ b/pxr/imaging/hd/dataSourceLocator.h @@ -302,9 +302,11 @@ class HdDataSourceLocatorSet /// two sets contains a prefix of the other set. HD_API bool Intersects(const HdDataSourceLocatorSet &locatorSet) const; + /// True if and only if this set contains no data source locator. HD_API bool IsEmpty() const; + /// True if the set (closed under descendancy) contains the given /// locator. /// @@ -313,6 +315,15 @@ class HdDataSourceLocatorSet HD_API bool Contains(const HdDataSourceLocator &locator) const; + /// Returns a lexicographically sorted locator set wherein locators in this + /// set that have \p oldPrefix as a prefix use \p newPrefix instead. The + /// returned set is closed under descendancy and may have equal or fewer + /// data source locators as a result. + HD_API + HdDataSourceLocatorSet ReplacePrefix( + const HdDataSourceLocator &oldPrefix, + const HdDataSourceLocator &newPrefix) const; + private: void _InsertAndDeleteSuffixes(_Locators::iterator *position, const HdDataSourceLocator &locator); diff --git a/pxr/imaging/hd/dataSourceMaterialNetworkInterface.cpp b/pxr/imaging/hd/dataSourceMaterialNetworkInterface.cpp index eb358f4fc3..aea11509d5 100644 --- a/pxr/imaging/hd/dataSourceMaterialNetworkInterface.cpp +++ b/pxr/imaging/hd/dataSourceMaterialNetworkInterface.cpp @@ -175,6 +175,52 @@ HdDataSourceMaterialNetworkInterface::GetNodeType( return TfToken(); } +HdContainerDataSourceHandle +HdDataSourceMaterialNetworkInterface::_GetNodeTypeInfo(const TfToken& nodeName) const +{ + if (HdContainerDataSourceHandle node = _GetNode(nodeName)) { + return HdContainerDataSource::Cast( + node->Get(HdMaterialNodeSchemaTokens->renderContextNodeIdentifiers)); + } + return nullptr; +} + +TfTokenVector +HdDataSourceMaterialNetworkInterface::GetNodeTypeInfoKeys( + const TfToken& nodeName) const +{ + static const std::string infoNamespacePrefix("info:"); + + TfTokenVector ret; + if (HdContainerDataSourceHandle nodeTypeInfo + = _GetNodeTypeInfo(nodeName)) { + for (const TfToken& name : nodeTypeInfo->GetNames()) { + const std::string& nameStr = name.GetString(); + if (!TfStringStartsWith(nameStr, infoNamespacePrefix)) { + continue; + } + const std::string nameWithoutInfo + = nameStr.substr(infoNamespacePrefix.size()); + ret.push_back(TfToken(nameWithoutInfo)); + } + } + return ret; +} + +VtValue +HdDataSourceMaterialNetworkInterface::GetNodeTypeInfoValue( + const TfToken& nodeName, const TfToken& key) const +{ + if (HdContainerDataSourceHandle nodeTypeInfo = _GetNodeTypeInfo(nodeName)) { + if (HdSampledDataSourceHandle value + = HdSampledDataSource::Cast(nodeTypeInfo->Get(TfToken( + TfStringPrintf("info:%s", key.GetText()))))) { + return value->GetValue(0.f); + } + } + return VtValue(); +} + TfTokenVector HdDataSourceMaterialNetworkInterface::GetAuthoredNodeParameterNames( const TfToken &nodeName) const @@ -228,7 +274,7 @@ HdDataSourceMaterialNetworkInterface::GetNodeParameterValue( HdSampledDataSource::Cast(it->second)) { return sds->GetValue(0.0f); } else { - // overriden with nullptr data source means deletion + // overridden with nullptr data source means deletion return VtValue(); } } diff --git a/pxr/imaging/hd/dataSourceMaterialNetworkInterface.h b/pxr/imaging/hd/dataSourceMaterialNetworkInterface.h index 4d5dffa923..0583bd7f97 100644 --- a/pxr/imaging/hd/dataSourceMaterialNetworkInterface.h +++ b/pxr/imaging/hd/dataSourceMaterialNetworkInterface.h @@ -1,4 +1,3 @@ - // // Copyright 2021 Pixar // @@ -63,6 +62,12 @@ class HdDataSourceMaterialNetworkInterface HD_API TfToken GetNodeType(const TfToken &nodeName) const override; + HD_API + TfTokenVector GetNodeTypeInfoKeys(const TfToken& nodeName) const override; + HD_API + VtValue GetNodeTypeInfoValue( + const TfToken& nodeName, const TfToken& value) const override; + HD_API TfTokenVector GetAuthoredNodeParameterNames( const TfToken &nodeName) const override; @@ -131,6 +136,9 @@ class HdDataSourceMaterialNetworkInterface HdContainerDataSourceHandle Finish(); private: + HdContainerDataSourceHandle _GetNodeTypeInfo( + const TfToken& nodeName) const; + using _OverrideMap = std::unordered_map; @@ -166,4 +174,4 @@ class HdDataSourceMaterialNetworkInterface PXR_NAMESPACE_CLOSE_SCOPE -#endif // HD_DATA_SOURCE_MATERIAL_NETWORK_INTERFACE_H \ No newline at end of file +#endif // HD_DATA_SOURCE_MATERIAL_NETWORK_INTERFACE_H diff --git a/pxr/imaging/hd/dependencyForwardingSceneIndex.h b/pxr/imaging/hd/dependencyForwardingSceneIndex.h index 9b263cc0d3..ddd14d10f5 100644 --- a/pxr/imaging/hd/dependencyForwardingSceneIndex.h +++ b/pxr/imaging/hd/dependencyForwardingSceneIndex.h @@ -224,6 +224,7 @@ class HdDependencyForwardingSceneIndex // // NOTE: optional arguments are in service of unit testing to provide // insight in to what was removed. + HD_API void RemoveDeletedEntries( SdfPathVector *removedAffectedPrimPaths = nullptr, SdfPathVector *removedDependedOnPrimPaths = nullptr); diff --git a/pxr/imaging/hd/dirtyBitsTranslator.cpp b/pxr/imaging/hd/dirtyBitsTranslator.cpp index bc00b3c7ae..9e6c372841 100644 --- a/pxr/imaging/hd/dirtyBitsTranslator.cpp +++ b/pxr/imaging/hd/dirtyBitsTranslator.cpp @@ -259,16 +259,8 @@ HdDirtyBitsTranslator::SprimDirtyBitsToLocatorSet(TfToken const& primType, if (bits & HdLight::DirtyResource) { set->append(HdMaterialSchema::GetDefaultLocator()); } - // XXX: Right now, primvars don't seem to have a dirty bit, so - // group them with params... if (bits & HdLight::DirtyParams) { set->append(HdPrimvarsSchema::GetDefaultLocator()); - } - // XXX: Some delegates seems to be sending light visibility - // changes on the child guide mesh instead of here. So, - // for now, let's consider dirty light params to include - // light visibility - if (bits & (HdChangeTracker::DirtyVisibility | HdLight::DirtyParams)) { set->append(HdVisibilitySchema::GetDefaultLocator()); } if (bits & HdLight::DirtyTransform) { @@ -719,7 +711,7 @@ HdDirtyBitsTranslator::SprimLocatorSetToDirtyBits( bits |= HdLight::DirtyParams; } if (_FindLocator(HdVisibilitySchema::GetDefaultLocator(), end, &it)) { - bits |= HdChangeTracker::DirtyVisibility | HdLight::DirtyParams; + bits |= HdLight::DirtyParams; } if (_FindLocator(HdXformSchema::GetDefaultLocator(), end, &it)) { bits |= HdLight::DirtyTransform; diff --git a/pxr/imaging/hd/extComputationUtils.cpp b/pxr/imaging/hd/extComputationUtils.cpp index 2a276c07c1..842120aabb 100644 --- a/pxr/imaging/hd/extComputationUtils.cpp +++ b/pxr/imaging/hd/extComputationUtils.cpp @@ -190,12 +190,21 @@ _ExecuteComputations(HdExtComputationConstPtrVector computations, // the value store. Hd_ExtComputationContextInternal context; for (auto const& sceneInput : comp->GetSceneInputNames()) { - context.SetInputValue(sceneInput, valueStore.at(sceneInput)); + auto const it = valueStore.find(sceneInput); + if (it != valueStore.end()) { + context.SetInputValue(sceneInput, it->second); + } + // Avoid issuing a warning here since we do so in the computation + // context and typically in the CPU kernel. } for (auto const& computedInput : comp->GetComputationInputs()) { - context.SetInputValue(computedInput.name, - valueStore.at(computedInput.sourceComputationOutputName)); + TfToken const &key = computedInput.sourceComputationOutputName; + auto const it = valueStore.find(key); + if (it != valueStore.end()) { + context.SetInputValue(computedInput.name, it->second); + } + // See comment above on suppressing the warning here. } sceneDelegate->InvokeExtComputation(compId, &context); diff --git a/pxr/imaging/hd/filteringSceneIndex.cpp b/pxr/imaging/hd/filteringSceneIndex.cpp index 5554f9b0b3..3bfdc2ac9c 100644 --- a/pxr/imaging/hd/filteringSceneIndex.cpp +++ b/pxr/imaging/hd/filteringSceneIndex.cpp @@ -27,14 +27,40 @@ PXR_NAMESPACE_OPEN_SCOPE +// This is a fallback scene index that we use in case an invalid sceneIndex is +// passed in to the filtering scene index c'tor. +class _NoOpSceneIndex final : public HdSceneIndexBase +{ +public: + static HdSceneIndexBaseRefPtr New() + { + return TfCreateRefPtr(new _NoOpSceneIndex()); + } + + HdSceneIndexPrim GetPrim(const SdfPath& primPath) const override final + { + return { TfToken(), nullptr }; + } + + SdfPathVector + GetChildPrimPaths(const SdfPath& priMPath) const override final + { + return {}; + } +}; + HdSingleInputFilteringSceneIndexBase::HdSingleInputFilteringSceneIndexBase( - const HdSceneIndexBaseRefPtr &inputSceneIndex) -: _inputSceneIndex(inputSceneIndex) -, _observer(this) + const HdSceneIndexBaseRefPtr& inputSceneIndex) + : _inputSceneIndex(inputSceneIndex) + , _observer(this) { if (inputSceneIndex) { inputSceneIndex->AddObserver(HdSceneIndexObserverPtr(&_observer)); } + else { + TF_CODING_ERROR("Invalid input sceneIndex."); + _inputSceneIndex = _NoOpSceneIndex::New(); + } } std::vector diff --git a/pxr/imaging/hd/filteringSceneIndex.h b/pxr/imaging/hd/filteringSceneIndex.h index f51a0d2ab1..6fdf49dc9a 100644 --- a/pxr/imaging/hd/filteringSceneIndex.h +++ b/pxr/imaging/hd/filteringSceneIndex.h @@ -63,7 +63,7 @@ TF_DECLARE_WEAK_AND_REF_PTRS(HdSingleInputFilteringSceneIndexBase); /// /// An abstract base class for a filtering scene index that observes a single /// input scene index. -/// +/// class HdSingleInputFilteringSceneIndexBase : public HdFilteringSceneIndexBase { public: @@ -87,6 +87,10 @@ class HdSingleInputFilteringSceneIndexBase : public HdFilteringSceneIndexBase const HdSceneIndexBase &sender, const HdSceneIndexObserver::DirtiedPrimEntries &entries) = 0; + /// Returns the input scene. + /// + /// It is always safe to call and dereference this return value. If this + /// was constructed with a null scene index, a fallback one will be used. const HdSceneIndexBaseRefPtr &_GetInputSceneIndex() const { return _inputSceneIndex; } diff --git a/pxr/imaging/hd/flatteningSceneIndex.cpp b/pxr/imaging/hd/flatteningSceneIndex.cpp index 1cccc02236..aa0bbe4260 100644 --- a/pxr/imaging/hd/flatteningSceneIndex.cpp +++ b/pxr/imaging/hd/flatteningSceneIndex.cpp @@ -28,6 +28,8 @@ #include "pxr/imaging/hd/xformSchema.h" #include "pxr/imaging/hd/purposeSchema.h" #include "pxr/imaging/hd/visibilitySchema.h" +#include "pxr/imaging/hd/materialBindingSchema.h" +#include "pxr/imaging/hd/instancedBySchema.h" #include "pxr/base/trace/trace.h" #include "pxr/base/work/utils.h" @@ -143,7 +145,9 @@ HdFlatteningSceneIndex::_PrimsAdded( HdXformSchema::GetDefaultLocator(), HdVisibilitySchema::GetDefaultLocator(), HdPurposeSchema::GetDefaultLocator(), - HdDataSourceLocator(_tokens->model) + _GetDrawModeLocator(), + HdMaterialBindingSchema::GetDefaultLocator(), + HdInstancedBySchema::GetDefaultLocator() }; _DirtyHierarchy(entry.primPath, locators, &dirtyEntries); @@ -205,12 +209,18 @@ HdFlatteningSceneIndex::_PrimsDirtied( HdPurposeSchema::GetDefaultLocator())) { locators.insert(HdPurposeSchema::GetDefaultLocator()); } - static const HdDataSourceLocator modelLocator(_tokens->model); + if (entry.dirtyLocators.Intersects(_GetDrawModeLocator())) { + locators.insert(_GetDrawModeLocator()); + } if (entry.dirtyLocators.Intersects( - modelLocator)) { - locators.insert(modelLocator); + HdMaterialBindingSchema::GetDefaultLocator())) { + locators.insert(HdMaterialBindingSchema::GetDefaultLocator()); } - + if (entry.dirtyLocators.Intersects( + HdInstancedBySchema::GetDefaultLocator())) { + locators.insert(HdInstancedBySchema::GetDefaultLocator()); + } + if (!locators.IsEmpty()) { _DirtyHierarchy(entry.primPath, locators, &dirtyEntries); } @@ -269,9 +279,6 @@ _PrimLevelWrappingDataSource::_PrimLevelWrappingDataSource( : _sceneIndex(scene) , _primPath(primPath) , _inputDataSource(inputDataSource) - , _computedXformDataSource(nullptr) - , _computedVisDataSource(nullptr) - , _computedPurposeDataSource(nullptr) { } @@ -289,6 +296,7 @@ HdFlatteningSceneIndex::_PrimLevelWrappingDataSource::PrimDirtied( bool anyDirtied = false; static const HdContainerDataSourceHandle containerNull(nullptr); static const HdTokenDataSourceHandle tokenNull(nullptr); + static const HdDataSourceBaseHandle baseNull(nullptr); if (set.Intersects(HdXformSchema::GetDefaultLocator())) { if (HdContainerDataSource::AtomicLoad(_computedXformDataSource)) { @@ -316,6 +324,16 @@ HdFlatteningSceneIndex::_PrimLevelWrappingDataSource::PrimDirtied( HdTokenDataSource::AtomicStore( _computedDrawModeDataSource, tokenNull); } + if (set.Intersects(HdMaterialBindingSchema::GetDefaultLocator())) { + anyDirtied = true; + HdDataSourceBase::AtomicStore( + _computedMaterialBindingDataSource, baseNull); + } + if (set.Intersects(HdInstancedBySchema::GetDefaultLocator())) { + anyDirtied = true; + HdDataSourceBase::AtomicStore( + _computedInstancedByDataSource, baseNull); + } return anyDirtied; } @@ -337,6 +355,14 @@ HdFlatteningSceneIndex::_PrimLevelWrappingDataSource::Has( return true; } + if (name == HdMaterialBindingSchemaTokens->materialBinding) { + return true; + } + + if (name == HdInstancedBySchemaTokens->instancedBy) { + return true; + } + if (!_inputDataSource) { return false; } @@ -356,6 +382,8 @@ HdFlatteningSceneIndex::_PrimLevelWrappingDataSource::GetNames() bool hasVis = false; bool hasPurpose = false; bool hasModel = false; + bool hasMaterialBinding = false; + bool hasInstancedBy = false; for (const TfToken &name : result) { if (name == HdXformSchemaTokens->xform) { hasXform = true; @@ -369,7 +397,14 @@ HdFlatteningSceneIndex::_PrimLevelWrappingDataSource::GetNames() if (name == _tokens->model) { hasModel = true; } - if (hasXform && hasVis && hasPurpose && hasModel) { + if (name == HdMaterialBindingSchemaTokens->materialBinding) { + hasMaterialBinding = true; + } + if (name == HdInstancedBySchemaTokens->instancedBy) { + hasInstancedBy = true; + } + if (hasXform && hasVis && hasPurpose && hasModel + && hasMaterialBinding && hasInstancedBy) { break; } } @@ -386,11 +421,19 @@ HdFlatteningSceneIndex::_PrimLevelWrappingDataSource::GetNames() if (!hasModel) { result.push_back(_tokens->model); } + if (!hasMaterialBinding) { + result.push_back(HdMaterialBindingSchemaTokens->materialBinding); + } + if (!hasInstancedBy) { + result.push_back(HdInstancedBySchemaTokens->instancedBy); + } } else { result.push_back(HdXformSchemaTokens->xform); result.push_back(HdVisibilitySchemaTokens->visibility); result.push_back(HdPurposeSchemaTokens->purpose); result.push_back(_tokens->model); + result.push_back(HdMaterialBindingSchemaTokens->materialBinding); + result.push_back(HdInstancedBySchemaTokens->instancedBy); } return result; @@ -408,6 +451,10 @@ HdFlatteningSceneIndex::_PrimLevelWrappingDataSource::Get( return _GetPurpose(); } else if (name == _tokens->model) { return _GetModel(); + } else if (name == HdMaterialBindingSchemaTokens->materialBinding) { + return _GetMaterialBinding(); + } else if (name == HdInstancedBySchemaTokens->instancedBy) { + return _GetInstancedBy(); } else if (_inputDataSource) { return _inputDataSource->Get(name); } else { @@ -648,4 +695,109 @@ HdFlatteningSceneIndex::_PrimLevelWrappingDataSource::_GetDrawModeUncached( return _sceneIndex._identityDrawMode; } +HdDataSourceBaseHandle +HdFlatteningSceneIndex::_PrimLevelWrappingDataSource::_GetMaterialBinding() +{ + HdDataSourceBaseHandle result = + HdDataSourceBase::AtomicLoad(_computedMaterialBindingDataSource); + + if (!result) { + result = _GetMaterialBindingUncached(); + HdDataSourceBase::AtomicStore(_computedMaterialBindingDataSource, + result); + } + + // The cached value of the absence of a materialBinding is a non-container + // data source. + return HdContainerDataSource::Cast(result); +} + +HdDataSourceBaseHandle +HdFlatteningSceneIndex::_PrimLevelWrappingDataSource:: +_GetMaterialBindingUncached() +{ + HdContainerDataSourceHandle inputBindings = + HdMaterialBindingSchema::GetFromParent(_inputDataSource).GetContainer(); + + HdContainerDataSourceHandle parentBindings; + if (_primPath.GetPathElementCount()) { + SdfPath parentPath = _primPath.GetParentPath(); + const auto it = _sceneIndex._prims.find(parentPath); + if (it != _sceneIndex._prims.end()) { + parentBindings = HdMaterialBindingSchema::GetFromParent( + it->second.prim.dataSource).GetContainer(); + } + } + + if (inputBindings) { + if (parentBindings) { + // Parent and local bindings might have unique fields so we must + // overlay them. If we are concerned about overlay depth, we could + // compare GetNames() results to decide whether the child bindings + // completely mask the parent. + return HdOverlayContainerDataSource::New( + inputBindings, parentBindings); + } + + return inputBindings; + } else { + if (parentBindings) { + return parentBindings; + } else { + // Cache the absence of value by storing a non-container which will + // fail the cast on return. Using retained "false" because its New + // returns a shared instance rather than a new allocation. + return HdRetainedTypedSampledDataSource::New(false); + } + } +} + +HdDataSourceBaseHandle +HdFlatteningSceneIndex::_PrimLevelWrappingDataSource::_GetInstancedBy() +{ + HdDataSourceBaseHandle result = + HdDataSourceBase::AtomicLoad(_computedInstancedByDataSource); + + if (!result) { + result = _GetInstancedByUncached(); + HdDataSourceBase::AtomicStore( + _computedInstancedByDataSource, result); + } + + // The cached value of the absence of a materialBinding is a non-container + // data source. + return HdContainerDataSource::Cast(result); +} + +HdDataSourceBaseHandle +HdFlatteningSceneIndex::_PrimLevelWrappingDataSource::_GetInstancedByUncached() +{ + if (HdInstancedBySchema schema = + HdInstancedBySchema::GetFromParent(_inputDataSource)) { + if (HdPathArrayDataSourceHandle const pathsSrc = schema.GetPaths()) { + if (!pathsSrc->GetTypedValue(0.0f).empty()) { + return schema.GetContainer(); + } + } + } + + if (_primPath.GetPathElementCount() == 0) { + return HdRetainedTypedSampledDataSource::New(false); + } + + const SdfPath parentPath = _primPath.GetParentPath(); + const auto it = _sceneIndex._prims.find(parentPath); + if (it == _sceneIndex._prims.end()) { + return HdRetainedTypedSampledDataSource::New(false); + } + + HdContainerDataSourceHandle const parentDs = + HdInstancedBySchema::GetFromParent( + it->second.prim.dataSource).GetContainer(); + if (!parentDs) { + return HdRetainedTypedSampledDataSource::New(false); + } + return parentDs; +} + PXR_NAMESPACE_CLOSE_SCOPE diff --git a/pxr/imaging/hd/flatteningSceneIndex.h b/pxr/imaging/hd/flatteningSceneIndex.h index ca583686a6..e3c2893bb1 100644 --- a/pxr/imaging/hd/flatteningSceneIndex.h +++ b/pxr/imaging/hd/flatteningSceneIndex.h @@ -139,6 +139,10 @@ class HdFlatteningSceneIndex : public HdSingleInputFilteringSceneIndexBase const HdContainerDataSourceHandle &model); HdTokenDataSourceHandle _GetDrawModeUncached( const HdContainerDataSourceHandle &model); + HdDataSourceBaseHandle _GetMaterialBinding(); + HdDataSourceBaseHandle _GetMaterialBindingUncached(); + HdDataSourceBaseHandle _GetInstancedBy(); + HdDataSourceBaseHandle _GetInstancedByUncached(); const HdFlatteningSceneIndex &_sceneIndex; SdfPath _primPath; @@ -147,6 +151,13 @@ class HdFlatteningSceneIndex : public HdSingleInputFilteringSceneIndexBase HdContainerDataSourceAtomicHandle _computedVisDataSource; HdContainerDataSourceAtomicHandle _computedPurposeDataSource; HdTokenDataSource::AtomicHandle _computedDrawModeDataSource; + + // Stored as a base rather than a container so we can use cast to + // distinguish between a cached value and the absence of a cached value. + // Internally, this is set to a retained bool=false data source to + // indicate that no binding is present. + HdDataSourceBaseAtomicHandle _computedMaterialBindingDataSource; + HdDataSourceBaseAtomicHandle _computedInstancedByDataSource; }; HD_DECLARE_DATASOURCE_HANDLES(_PrimLevelWrappingDataSource); diff --git a/pxr/imaging/hd/instanceRegistry.h b/pxr/imaging/hd/instanceRegistry.h index 77326c5d11..c526364185 100644 --- a/pxr/imaging/hd/instanceRegistry.h +++ b/pxr/imaging/hd/instanceRegistry.h @@ -172,6 +172,15 @@ class HdInstanceRegistry { /// be recycled if they are needed again. size_t GarbageCollect(int recycleCount = 0); + /// Removes unreferenced entries and returns the count + /// of remaining entries. If an entry is to be removed, callback function + /// "callback" will be called on the entry before removal. When + /// recycleCount is greater than zero, unreferenced entries will not be + /// removed until GarbageCollect() is called that many more times, i.e. + /// allowing unreferenced entries to be recycled if they are needed again. + template + size_t GarbageCollect(Callback &&callback, int recycleCount = 0); + /// Returns a const iterator being/end of dictionary. Mainly used for /// resource auditing. typedef typename InstanceType::Dictionary::const_iterator const_iterator; @@ -246,6 +255,15 @@ HdInstanceRegistry::FindInstance( template size_t HdInstanceRegistry::GarbageCollect(int recycleCount) +{ + // Call GarbageCollect with empty callback function + return GarbageCollect([](void*){}, recycleCount); +} + +template +template +size_t +HdInstanceRegistry::GarbageCollect(Callback &&callback, int recycleCount) { HD_TRACE_FUNCTION(); HF_MALLOC_TAG_FUNCTION(); @@ -262,6 +280,7 @@ HdInstanceRegistry::GarbageCollect(int recycleCount) // erase instance which isn't referred from anyone bool isUnique = _IsUnique(it->second.value); if (isUnique && (++it->second.recycleCounter > recycleCount)) { + std::forward(callback)(it->second.value.get()); it = _dictionary.unsafe_erase(it); } else { ++it; diff --git a/pxr/imaging/hd/instancedBySceneIndex.cpp b/pxr/imaging/hd/instancedBySceneIndex.cpp new file mode 100644 index 0000000000..00c89a23f0 --- /dev/null +++ b/pxr/imaging/hd/instancedBySceneIndex.cpp @@ -0,0 +1,361 @@ +// +// Copyright 2022 Pixar +// +// Licensed under the Apache License, Version 2.0 (the "Apache License") +// with the following modification; you may not use this file except in +// compliance with the Apache License and the following modification to it: +// Section 6. Trademarks. is deleted and replaced with: +// +// 6. Trademarks. This License does not grant permission to use the trade +// names, trademarks, service marks, or product names of the Licensor +// and its affiliates, except as required to comply with Section 4(c) of +// the License and to reproduce the content of the NOTICE file. +// +// You may obtain a copy of the Apache License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the Apache License with the above modification is +// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the Apache License for the specific +// language governing permissions and limitations under the Apache License. +// +#include "pxr/imaging/hd/instancedBySceneIndex.h" +#include "pxr/imaging/hd/retainedDataSource.h" +#include "pxr/imaging/hd/tokens.h" +#include "pxr/imaging/hd/instancedBySchema.h" +#include "pxr/imaging/hd/instancerTopologySchema.h" +#include "pxr/imaging/hd/lazyContainerDataSource.h" +#include "pxr/imaging/hd/overlayContainerDataSource.h" +#include "pxr/base/trace/trace.h" + +PXR_NAMESPACE_OPEN_SCOPE + +namespace { + +// VtArray has no insert +void +_Insert(VtArray &paths, + const VtArray::const_iterator &it, + const SdfPath &path) +{ + // Iterators are invalidated by mutating functions on VtArray. + const size_t i = it - paths.begin(); + paths.resize(paths.size() + 1); + std::move_backward(paths.begin() + i, paths.end() - 1, paths.end()); + paths[i] = path; +} + +// Maps a prim to all the instancers pointing at it. +// +class _PrototypeToInstancerMapping { +public: + // Returns instancers that point to this prim in lexicographic + // order. + // + // Returns empty array if no instancer is pointing at this prim + // (and thus the prim is not a prototype). + const VtArray & + GetInstancersForPrim(const SdfPath &primPath) const + { + TRACE_FUNCTION(); + + const auto it = _prototypeToInstancerMap.find(primPath); + if (it == _prototypeToInstancerMap.end()) { + static const VtArray empty; + return empty; + } + return it->second; + } + + // Adds entries prototype -> instancer to map. + void AddPrototypes( + const SdfPath &instancer, + const VtArray &prototypes) + { + for (const SdfPath &prototype : prototypes) { + VtArray &instancers = _prototypeToInstancerMap[prototype]; + const auto it = std::lower_bound( + instancers.cbegin(), instancers.cend(), instancer); + if (it != instancers.end() && (*it) == instancer) { + continue; + } + _Insert(instancers, it, instancer); + } + } + + // Remove entries prototype -> instancer to map. + void RemovePrototypes( + const SdfPath &instancer, + const VtArray &prototypes) + { + TRACE_FUNCTION(); + + for (const SdfPath &prototype : prototypes) { + const auto it = _prototypeToInstancerMap.find(prototype); + if (it == _prototypeToInstancerMap.end()) { + continue; + } + VtArray &instancers = it->second; + const auto instancerIt = std::lower_bound( + instancers.cbegin(), instancers.cend(), instancer); + if (instancerIt == instancers.cend()) { + continue; + } + instancers.erase(instancerIt); + if (instancers.empty()) { + _prototypeToInstancerMap.erase(it); + } + } + } + +private: + using _PathToPathsMap = std::map>; + _PathToPathsMap _prototypeToInstancerMap; +}; + +void +_Append(const VtArray &newPaths, SdfPathSet * const paths) +{ + if (paths) { + paths->insert(newPaths.begin(), newPaths.end()); + } +} + +} + +class HdInstancedBySceneIndex::InstancerMapping +{ +public: + // Returns instancers for given primPath. + // + const VtArray & + GetInstancersForPrim(const SdfPath &primPath) const { + return _prototypeToInstancerMapping.GetInstancersForPrim(primPath); + } + + // Updates the mapping by clearing the old prototypes for the instancer + // and setting the new ones. + // + // Optionally, gives the set of prims for which "instancedBy/paths" + // has changed. + // + void SetPrototypesForInstancer( + const SdfPath &instancer, + const VtArray &newPrototypes, + SdfPathSet * const dirtiedPrims) + { + TRACE_FUNCTION(); + + VtArray &prototypes = _instancerToPrototypeMap[instancer]; + _prototypeToInstancerMapping.RemovePrototypes(instancer, prototypes); + _Append(prototypes, dirtiedPrims); + prototypes = newPrototypes; + _prototypeToInstancerMapping.AddPrototypes(instancer, prototypes); + _Append(prototypes, dirtiedPrims); + } + + // Updates the map by removing all instancers with prefix primPath. + // + // Optionally, gives a set of prims like SetPrototypesForInstancer. + // + void RemoveInstancersUnderPrim( + const SdfPath &primPath, + SdfPathSet * const dirtiedPrims) + { + TRACE_FUNCTION(); + + auto it = _instancerToPrototypeMap.lower_bound(primPath); + while (it != _instancerToPrototypeMap.end() && + it->first.HasPrefix(primPath)) { + _prototypeToInstancerMapping.RemovePrototypes( + it->first, it->second); + _Append(it->second, dirtiedPrims); + it = _instancerToPrototypeMap.erase(it); + } + } + +private: + + std::map> _instancerToPrototypeMap; + _PrototypeToInstancerMapping _prototypeToInstancerMapping; +}; + +// ---------------------------------------------------------------------------- + +static +HdContainerDataSourceHandle +_InstancedByPathsDataSource( + const SdfPath &primPath, + const HdInstancedBySceneIndex::InstancerMappingSharedPtr &mapping) +{ + using PathsDataSource = HdRetainedTypedSampledDataSource>; + + return + HdInstancedBySchema::Builder() + .SetPaths( + PathsDataSource::New( + mapping->GetInstancersForPrim(primPath))) + .Build(); +} + +HdInstancedBySceneIndex::HdInstancedBySceneIndex( + const HdSceneIndexBaseRefPtr &inputScene) + : HdSingleInputFilteringSceneIndexBase(inputScene) + , _instancerMapping(std::make_shared()) +{ +} + +HdInstancedBySceneIndex::~HdInstancedBySceneIndex() = default; + +HdSceneIndexPrim +HdInstancedBySceneIndex::GetPrim( + const SdfPath &primPath) const +{ + TRACE_FUNCTION(); + + const HdSceneIndexPrim prim = _GetInputSceneIndex()->GetPrim(primPath); + + return { + prim.primType, + HdOverlayContainerDataSource::New( + prim.dataSource, + HdRetainedContainerDataSource::New( + HdInstancedBySchemaTokens->instancedBy, + HdLazyContainerDataSource::New( + std::bind(_InstancedByPathsDataSource, + primPath, _instancerMapping)))) }; +} + +SdfPathVector +HdInstancedBySceneIndex::GetChildPrimPaths( + const SdfPath &primPath) const +{ + return _GetInputSceneIndex()->GetChildPrimPaths(primPath); +} + +VtArray +HdInstancedBySceneIndex::_GetPrototypes(const SdfPath &instancer) const +{ + HdSceneIndexPrim const prim = _GetInputSceneIndex()->GetPrim(instancer); + HdInstancerTopologySchema instancerTopology = + HdInstancerTopologySchema::GetFromParent(prim.dataSource); + if (HdPathArrayDataSourceHandle const ds = + instancerTopology.GetPrototypes()) { + return ds->GetTypedValue(0.0f); + } + return { }; +} + +void +HdInstancedBySceneIndex::_SendInstancedByDirtied(const SdfPathSet &paths) +{ + if (paths.empty()) { + return; + } + + static const HdDataSourceLocatorSet instancedByLocator{ + HdInstancedBySchema::GetDefaultLocator()}; + + HdSceneIndexObserver::DirtiedPrimEntries dirtyEntries; + dirtyEntries.reserve(paths.size()); + for (const SdfPath &path : paths) { + dirtyEntries.emplace_back(path, instancedByLocator); + } + _SendPrimsDirtied(dirtyEntries); +} + +void +HdInstancedBySceneIndex::_PrimsAdded( + const HdSceneIndexBase &sender, + const HdSceneIndexObserver::AddedPrimEntries &entries) +{ + const bool isObserved = _IsObserved(); + + SdfPathSet dirtiedPrims; + + // Add new instancers to the instancer mapping table. + for (const HdSceneIndexObserver::AddedPrimEntry &entry : entries) { + if (entry.primType == HdPrimTypeTokens->instancer) { + _instancerMapping->SetPrototypesForInstancer( + entry.primPath, + _GetPrototypes(entry.primPath), + isObserved ? &dirtiedPrims : nullptr); + } + } + + if (!isObserved) { + return; + } + + _SendPrimsAdded(entries); + _SendInstancedByDirtied(dirtiedPrims); +} + +void +HdInstancedBySceneIndex::_PrimsDirtied( + const HdSceneIndexBase &sender, + const HdSceneIndexObserver::DirtiedPrimEntries &entries) +{ + const bool isObserved = _IsObserved(); + + static const HdDataSourceLocator prototypesLocator = + HdInstancerTopologySchema::GetDefaultLocator().Append( + HdInstancerTopologySchemaTokens->prototypes); + + SdfPathSet dirtiedPrims; + for (const HdSceneIndexObserver::DirtiedPrimEntry &entry : entries) { + if (entry.dirtyLocators.Contains(prototypesLocator)) { + _instancerMapping->SetPrototypesForInstancer( + entry.primPath, + _GetPrototypes(entry.primPath), + isObserved ? &dirtiedPrims : nullptr); + } + } + if (!isObserved) { + return; + } + + // Pass along the dirty notification. + _SendPrimsDirtied(entries); + + _SendInstancedByDirtied(dirtiedPrims); +} + +void +HdInstancedBySceneIndex::_PrimsRemoved( + const HdSceneIndexBase &sender, + const HdSceneIndexObserver::RemovedPrimEntries &entries) +{ + const bool isObserved = _IsObserved(); + + SdfPathSet dirtiedPrims; + + for (const HdSceneIndexObserver::RemovedPrimEntry &entry : entries) { + _instancerMapping->RemoveInstancersUnderPrim( + entry.primPath, isObserved ? &dirtiedPrims : nullptr); + } + + if (!isObserved) { + return; + } + + _SendPrimsRemoved(entries); + + if (dirtiedPrims.empty()) { + return; + } + + // We do not send out dirtied messages for prims that w just removed. + for (const HdSceneIndexObserver::RemovedPrimEntry &entry : entries) { + auto it = dirtiedPrims.lower_bound(entry.primPath); + while (it != dirtiedPrims.end() && it->HasPrefix(entry.primPath)) { + it = dirtiedPrims.erase(it); + } + } + + _SendInstancedByDirtied(dirtiedPrims); +} + +PXR_NAMESPACE_CLOSE_SCOPE diff --git a/pxr/imaging/hd/instancedBySceneIndex.h b/pxr/imaging/hd/instancedBySceneIndex.h new file mode 100644 index 0000000000..238124f1d7 --- /dev/null +++ b/pxr/imaging/hd/instancedBySceneIndex.h @@ -0,0 +1,107 @@ +// +// Copyright 2022 Pixar +// +// Licensed under the Apache License, Version 2.0 (the "Apache License") +// with the following modification; you may not use this file except in +// compliance with the Apache License and the following modification to it: +// Section 6. Trademarks. is deleted and replaced with: +// +// 6. Trademarks. This License does not grant permission to use the trade +// names, trademarks, service marks, or product names of the Licensor +// and its affiliates, except as required to comply with Section 4(c) of +// the License and to reproduce the content of the NOTICE file. +// +// You may obtain a copy of the Apache License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the Apache License with the above modification is +// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the Apache License for the specific +// language governing permissions and limitations under the Apache License. +// +#ifndef PXR_IMAGING_HD_INSTANCED_BY_SCENE_INDEX_H +#define PXR_IMAGING_HD_INSTANCED_BY_SCENE_INDEX_H + +#include "pxr/imaging/hd/filteringSceneIndex.h" + +PXR_NAMESPACE_OPEN_SCOPE + +// This scene index gathers "prototypes" declarations from instancer prims, +// and uses them to define a synthetic attribute "instancedBy/paths", answering +// the question "Which instancers list me as a prototype?". +// +// To do this, the scene index inverts the "instancerTopology/prototypes" +// relationship of all instancers. That is, "instancedBy/paths" of a prim +// returns the paths of all instancers that have one of the +// "instancerTopology/prototypes" pointing to that prim. +// +// Note that if an instancer points to a prototype prim, we expect that +// instances all namespace descendants of the prototype prim (except for those +// subtrees that are targeted by a nested instancer). This behavior is not +// implemented here but by the flattening scene index. To determine the +// "instancedBy/paths" of a prim, the flattening scene index traverses the +// namespace ancestors of that prim starting with the prim itself until a +// non-empty list of "instancedBy/paths" (or the pseudo-root) is hit. +// +// Note that having more than one path in instancedBy/paths means that there +// are several (sibling) instancers instancing the same prim, not that the +// instancers are (necessarily) nested. In other words, to find all nested +// instancers, a client has to recurse the instancers that instancedBy/paths +// points to. +// +TF_DECLARE_REF_PTRS(HdInstancedBySceneIndex); + +class HdInstancedBySceneIndex : public HdSingleInputFilteringSceneIndexBase +{ +public: + HD_API + static HdInstancedBySceneIndexRefPtr New( + const HdSceneIndexBaseRefPtr &inputScene) { + return TfCreateRefPtr(new HdInstancedBySceneIndex(inputScene)); + } + + HD_API + ~HdInstancedBySceneIndex() override; + + HD_API + HdSceneIndexPrim GetPrim(const SdfPath &primPath) const override; + HD_API + SdfPathVector GetChildPrimPaths(const SdfPath &primPath) const override; + + // Data shared between this scene index and the data sources it produces. + class InstancerMapping; + using InstancerMappingSharedPtr = std::shared_ptr; + +protected: + HdInstancedBySceneIndex(const HdSceneIndexBaseRefPtr &inputScene); + + void _PrimsAdded( + const HdSceneIndexBase &sender, + const HdSceneIndexObserver::AddedPrimEntries &entries) override; + + void _PrimsDirtied( + const HdSceneIndexBase &sender, + const HdSceneIndexObserver::DirtiedPrimEntries &entries) override; + + void _PrimsRemoved( + const HdSceneIndexBase &sender, + const HdSceneIndexObserver::RemovedPrimEntries &entries) override; + +private: + // Given a prim path, extracts prototype paths from instancer topology + // schema from input scene index. + VtArray _GetPrototypes( + const SdfPath &instancer) const; + + // Sends prims dirtied messages with data source locator instancedBy + // for all given prim paths to scene index observers. + void _SendInstancedByDirtied(const SdfPathSet &paths); + + InstancerMappingSharedPtr const _instancerMapping; +}; + +PXR_NAMESPACE_CLOSE_SCOPE + +#endif // PXR_IMAGING_HD_INSTANCED_BY_SCENE_INDEX_H diff --git a/pxr/imaging/hd/materialFilteringSceneIndexBase.cpp b/pxr/imaging/hd/materialFilteringSceneIndexBase.cpp index 34cb34cc30..00afa9f4cc 100644 --- a/pxr/imaging/hd/materialFilteringSceneIndexBase.cpp +++ b/pxr/imaging/hd/materialFilteringSceneIndexBase.cpp @@ -25,13 +25,14 @@ #include "pxr/imaging/hd/dataSourceMaterialNetworkInterface.h" #include "pxr/imaging/hd/materialSchema.h" +#include "pxr/imaging/hd/tokens.h" PXR_NAMESPACE_OPEN_SCOPE namespace { -class _MaterialDataSource : public HdContainerDataSource +class _MaterialDataSource final : public HdContainerDataSource { public: HD_DECLARE_DATASOURCE(_MaterialDataSource); @@ -89,18 +90,18 @@ class _MaterialDataSource : public HdContainerDataSource }; -class _PrimDataSource : public HdContainerDataSource +class _PrimDataSource final : public HdContainerDataSource { public: HD_DECLARE_DATASOURCE(_PrimDataSource); _PrimDataSource( + const HdMaterialFilteringSceneIndexBase* base, const HdContainerDataSourceHandle &input, - const SdfPath &primPath, - const HdMaterialFilteringSceneIndexBase::FilteringFnc &fnc) - : _input(input) + const SdfPath &primPath) + : _base(base) + , _input(input) , _primPath(primPath) - , _fnc(fnc) {} bool @@ -130,7 +131,7 @@ class _PrimDataSource : public HdContainerDataSource if (HdContainerDataSourceHandle materialContainer = HdContainerDataSource::Cast(result)) { return _MaterialDataSource::New( - materialContainer, _primPath, _fnc); + materialContainer, _primPath, _base->GetFilteringFunction()); } } return result; @@ -140,9 +141,11 @@ class _PrimDataSource : public HdContainerDataSource } private: + // pointer to HdMaterialFilteringSceneIndexBase so that we can query for the + // filtering function. + const HdMaterialFilteringSceneIndexBase* _base; HdContainerDataSourceHandle _input; SdfPath _primPath; - HdMaterialFilteringSceneIndexBase::FilteringFnc _fnc; }; @@ -158,28 +161,25 @@ HdMaterialFilteringSceneIndexBase::HdMaterialFilteringSceneIndexBase( HdSceneIndexPrim HdMaterialFilteringSceneIndexBase::GetPrim(const SdfPath &primPath) const { - if (auto input = _GetInputSceneIndex()) { - HdSceneIndexPrim prim = input->GetPrim(primPath); - if (prim.dataSource) { - prim.dataSource = _PrimDataSource::New(prim.dataSource, - primPath, _GetFilteringFunction()); - } - - return prim; + HdSceneIndexPrim prim = _GetInputSceneIndex()->GetPrim(primPath); + if (prim.primType == HdPrimTypeTokens->material && prim.dataSource) { + prim.dataSource = _PrimDataSource::New(this, prim.dataSource, primPath); } - return {TfToken(), nullptr}; + return prim; } SdfPathVector HdMaterialFilteringSceneIndexBase::GetChildPrimPaths( const SdfPath &primPath) const { - if (auto input = _GetInputSceneIndex()) { - return input->GetChildPrimPaths(primPath); - } + return _GetInputSceneIndex()->GetChildPrimPaths(primPath); +} - return {}; +HdMaterialFilteringSceneIndexBase::FilteringFnc +HdMaterialFilteringSceneIndexBase::GetFilteringFunction() const +{ + return _GetFilteringFunction(); } void diff --git a/pxr/imaging/hd/materialFilteringSceneIndexBase.h b/pxr/imaging/hd/materialFilteringSceneIndexBase.h index d284acc227..b9ccce65d5 100644 --- a/pxr/imaging/hd/materialFilteringSceneIndexBase.h +++ b/pxr/imaging/hd/materialFilteringSceneIndexBase.h @@ -53,6 +53,9 @@ class HdMaterialFilteringSceneIndexBase : using FilteringFnc = std::function; + HD_API + FilteringFnc GetFilteringFunction() const; + protected: virtual FilteringFnc _GetFilteringFunction() const = 0; diff --git a/pxr/imaging/hd/materialNetwork2Interface.cpp b/pxr/imaging/hd/materialNetwork2Interface.cpp index c12d08c7a7..4aeaff7c1f 100644 --- a/pxr/imaging/hd/materialNetwork2Interface.cpp +++ b/pxr/imaging/hd/materialNetwork2Interface.cpp @@ -92,6 +92,19 @@ HdMaterialNetwork2Interface::GetNodeType(const TfToken &nodeName) const return TfToken(); } +TfTokenVector +HdMaterialNetwork2Interface::GetNodeTypeInfoKeys(const TfToken& nodeName) const +{ + return {}; +} + +VtValue +HdMaterialNetwork2Interface::GetNodeTypeInfoValue( + const TfToken& nodeName, const TfToken& key) const +{ + return VtValue(); +} + TfTokenVector HdMaterialNetwork2Interface::GetAuthoredNodeParameterNames( const TfToken &nodeName) const @@ -271,4 +284,4 @@ HdMaterialNetwork2Interface::SetTerminalConnection( } } -PXR_NAMESPACE_CLOSE_SCOPE \ No newline at end of file +PXR_NAMESPACE_CLOSE_SCOPE diff --git a/pxr/imaging/hd/materialNetwork2Interface.h b/pxr/imaging/hd/materialNetwork2Interface.h index d314c90327..e81afa1f10 100644 --- a/pxr/imaging/hd/materialNetwork2Interface.h +++ b/pxr/imaging/hd/materialNetwork2Interface.h @@ -59,6 +59,13 @@ class HdMaterialNetwork2Interface HD_API TfToken GetNodeType(const TfToken &nodeName) const override; + HD_API + TfTokenVector GetNodeTypeInfoKeys(const TfToken& nodeName) const override; + + HD_API + VtValue GetNodeTypeInfoValue( + const TfToken& nodeName, const TfToken& key) const override; + HD_API TfTokenVector GetAuthoredNodeParameterNames( const TfToken &nodeName) const override; diff --git a/pxr/imaging/hd/materialNetworkInterface.h b/pxr/imaging/hd/materialNetworkInterface.h index b52a7f13cf..98a0b3075a 100644 --- a/pxr/imaging/hd/materialNetworkInterface.h +++ b/pxr/imaging/hd/materialNetworkInterface.h @@ -55,6 +55,16 @@ class HdMaterialNetworkInterface virtual TfTokenVector GetNodeNames() const = 0; virtual TfToken GetNodeType(const TfToken &nodeName) const = 0; + /// Node type info is a collection of data related to the node type, often + /// used to determine the node type. + /// + /// For now, we only have getters for this, as we aren't really intending on + /// mutating this in any filter. + virtual TfTokenVector + GetNodeTypeInfoKeys(const TfToken& nodeName) const = 0; + virtual VtValue + GetNodeTypeInfoValue(const TfToken& nodeName, const TfToken& key) const = 0; + virtual TfTokenVector GetAuthoredNodeParameterNames( const TfToken &nodeName) const = 0; @@ -119,4 +129,4 @@ class HdMaterialNetworkInterface PXR_NAMESPACE_CLOSE_SCOPE -#endif // PXR_IMAGING_HD_MATERIAL_NETWORK_INTERFACE_H \ No newline at end of file +#endif // PXR_IMAGING_HD_MATERIAL_NETWORK_INTERFACE_H diff --git a/pxr/imaging/hd/materialNodeSchema.cpp b/pxr/imaging/hd/materialNodeSchema.cpp index b1fbffb613..1dfa6e265b 100644 --- a/pxr/imaging/hd/materialNodeSchema.cpp +++ b/pxr/imaging/hd/materialNodeSchema.cpp @@ -59,16 +59,24 @@ HdMaterialNodeSchema::GetNodeIdentifier() HdMaterialNodeSchemaTokens->nodeIdentifier); } +HdContainerDataSourceHandle +HdMaterialNodeSchema::GetRenderContextNodeIdentifiers() +{ + return _GetTypedDataSource( + HdMaterialNodeSchemaTokens->renderContextNodeIdentifiers); +} + /*static*/ HdContainerDataSourceHandle HdMaterialNodeSchema::BuildRetained( const HdContainerDataSourceHandle ¶meters, const HdContainerDataSourceHandle &inputConnections, - const HdTokenDataSourceHandle &nodeIdentifier + const HdTokenDataSourceHandle &nodeIdentifier, + const HdContainerDataSourceHandle &renderContextNodeIdentifiers ) { - TfToken names[3]; - HdDataSourceBaseHandle values[3]; + TfToken names[4]; + HdDataSourceBaseHandle values[4]; size_t count = 0; if (parameters) { @@ -86,6 +94,11 @@ HdMaterialNodeSchema::BuildRetained( values[count++] = nodeIdentifier; } + if (renderContextNodeIdentifiers) { + names[count] = HdMaterialNodeSchemaTokens->renderContextNodeIdentifiers; + values[count++] = renderContextNodeIdentifiers; + } + return HdRetainedContainerDataSource::New(count, names, values); } @@ -114,13 +127,22 @@ HdMaterialNodeSchema::Builder::SetNodeIdentifier( return *this; } +HdMaterialNodeSchema::Builder & +HdMaterialNodeSchema::Builder::SetRenderContextNodeIdentifiers( + const HdContainerDataSourceHandle &renderContextNodeIdentifiers) +{ + _renderContextNodeIdentifiers = renderContextNodeIdentifiers; + return *this; +} + HdContainerDataSourceHandle HdMaterialNodeSchema::Builder::Build() { return HdMaterialNodeSchema::BuildRetained( _parameters, _inputConnections, - _nodeIdentifier + _nodeIdentifier, + _renderContextNodeIdentifiers ); } diff --git a/pxr/imaging/hd/materialNodeSchema.h b/pxr/imaging/hd/materialNodeSchema.h index 7d66c5e11b..9e39d99241 100644 --- a/pxr/imaging/hd/materialNodeSchema.h +++ b/pxr/imaging/hd/materialNodeSchema.h @@ -40,6 +40,7 @@ PXR_NAMESPACE_OPEN_SCOPE (parameters) \ (inputConnections) \ (nodeIdentifier) \ + (renderContextNodeIdentifiers) \ TF_DECLARE_PUBLIC_TOKENS(HdMaterialNodeSchemaTokens, HD_API, HDMATERIALNODE_SCHEMA_TOKENS); @@ -58,9 +59,25 @@ class HdMaterialNodeSchema : public HdSchema HdContainerDataSourceHandle GetParameters(); HD_API HdContainerDataSourceHandle GetInputConnections(); + + // This identifies the shader the node represents. The + // renderContextNodeIdentifier container can store alternative values for + // this. A consumer which is interested in a specific render context + // should check for that token within renderContextNodeIdentifiers and + // fall back on this value in its absence. HD_API HdTokenDataSourceHandle GetNodeIdentifier(); + // A shading node can hold a nodeIdentifier value for multiple render + // contexts at once. This allows multiple renderer target representations + // to coexist in the same renderable scene. The contents of this + // container are alternate possible values for nodeIdentifier. A consumer + // which is interested in a specific render context should check for that + // token within this container and fall back on nodeIdentifier in its + // absence. + HD_API + HdContainerDataSourceHandle GetRenderContextNodeIdentifiers(); + // RETRIEVING AND CONSTRUCTING /// Builds a container data source which includes the provided child data @@ -73,7 +90,8 @@ class HdMaterialNodeSchema : public HdSchema BuildRetained( const HdContainerDataSourceHandle ¶meters, const HdContainerDataSourceHandle &inputConnections, - const HdTokenDataSourceHandle &nodeIdentifier + const HdTokenDataSourceHandle &nodeIdentifier, + const HdContainerDataSourceHandle &renderContextNodeIdentifiers ); /// \class HdMaterialNodeSchema::Builder @@ -94,6 +112,9 @@ class HdMaterialNodeSchema : public HdSchema HD_API Builder &SetNodeIdentifier( const HdTokenDataSourceHandle &nodeIdentifier); + HD_API + Builder &SetRenderContextNodeIdentifiers( + const HdContainerDataSourceHandle &renderContextNodeIdentifiers); /// Returns a container data source containing the members set thus far. HD_API @@ -103,6 +124,7 @@ class HdMaterialNodeSchema : public HdSchema HdContainerDataSourceHandle _parameters; HdContainerDataSourceHandle _inputConnections; HdTokenDataSourceHandle _nodeIdentifier; + HdContainerDataSourceHandle _renderContextNodeIdentifiers; }; }; diff --git a/pxr/imaging/hd/mergingSceneIndex.cpp b/pxr/imaging/hd/mergingSceneIndex.cpp index 8a3e6c529d..31715ea8a8 100644 --- a/pxr/imaging/hd/mergingSceneIndex.cpp +++ b/pxr/imaging/hd/mergingSceneIndex.cpp @@ -131,10 +131,15 @@ HdMergingSceneIndex::GetPrim(const SdfPath &primPath) const for (const _InputEntry &entry : _inputs) { if (primPath.HasPrefix(entry.sceneRoot)) { HdSceneIndexPrim prim = entry.sceneIndex->GetPrim(primPath); + + // Use first non-empty prim type so that sparsely overlaid + // inputs can contribute data sources without defining type or type + // without providing a data source. + if (result.primType.IsEmpty() && !prim.primType.IsEmpty()) { + result.primType = prim.primType; + } + if (prim.dataSource) { - if (contributingDataSources.empty()) { - result.primType = prim.primType; - } contributingDataSources.push_back(prim.dataSource); } } @@ -193,7 +198,86 @@ HdMergingSceneIndex::_PrimsAdded( return; } - _SendPrimsAdded(entries); + // if there's only one input, no additional interpretation is required. + if (_inputs.size() < 2) { + _SendPrimsAdded(entries); + return; + } + + // Confirm that the type here is not masked by a stronger contributing + // input. We still send it along as an add because a weaker input providing + // potential data sources (at any container depth) does not directly + // indicate which data sources might be relevant. The trade-off is + // potential over-invalidation for correctness. This ensures that the + // primType is equivalent to what would be returned from GetPrim. + + HdSceneIndexObserver::AddedPrimEntries filteredEntries; + + for (const HdSceneIndexObserver::AddedPrimEntry &entry : entries) { + TfToken resolvedPrimType; + + for (const _InputEntry &inputEntry : _inputs) { + if (!entry.primPath.HasPrefix(inputEntry.sceneRoot)) { + continue; + } + + // If the primType is not empty and the first contributing data + // source is the sender, we don't need to query the type from + // any input. + if (get_pointer(inputEntry.sceneIndex) == &sender + && !entry.primType.IsEmpty()) { + break; + } + + // Get the type from the contributing input to see if it should be + // altered or omitted. + + const TfToken primType = + inputEntry.sceneIndex->GetPrim(entry.primPath).primType; + if (!primType.IsEmpty()) { + resolvedPrimType = primType; + break; + } + } + + bool replaceEntry = false; + if (!resolvedPrimType.IsEmpty()) { + if (resolvedPrimType != entry.primType) { + replaceEntry = true; + } + } + + if (replaceEntry) { + if (filteredEntries.empty()) { + // copy up to this entry + filteredEntries.reserve(entries.size()); + for (const HdSceneIndexObserver::AddedPrimEntry &origEntry : + entries) { + if (&origEntry == &entry) { + break; + } + filteredEntries.push_back(origEntry); + } + } + + // add altered entry + filteredEntries.emplace_back(entry.primPath, resolvedPrimType); + + } else { + // add unaltered entry if we've started to fill filteredEntries + // otherwise, do nothing as we are meaningful in the original + // entries until we need to copy some. + if (!filteredEntries.empty()) { + filteredEntries.push_back(entry); + } + } + } + + if (!filteredEntries.empty()) { + _SendPrimsAdded(filteredEntries); + } else { + _SendPrimsAdded(entries); + } } void diff --git a/pxr/imaging/hd/noticeBatchingSceneIndex.cpp b/pxr/imaging/hd/noticeBatchingSceneIndex.cpp index 8426199859..61c4f0fc9a 100644 --- a/pxr/imaging/hd/noticeBatchingSceneIndex.cpp +++ b/pxr/imaging/hd/noticeBatchingSceneIndex.cpp @@ -88,8 +88,6 @@ HdNoticeBatchingSceneIndex::_PrimsRemoved( if (_PrimsRemovedBatchEntry *batchEntry = dynamic_cast<_PrimsRemovedBatchEntry*>( _batches.back().get())) { - batchEntry->entries.reserve( - batchEntry->entries.size() + entries.size()); batchEntry->entries.insert( batchEntry->entries.end(), entries.begin(), entries.end()); return; @@ -117,8 +115,6 @@ HdNoticeBatchingSceneIndex::_PrimsDirtied( if (_PrimsDirtiedBatchEntry *batchEntry = dynamic_cast<_PrimsDirtiedBatchEntry*>( _batches.back().get())) { - batchEntry->entries.reserve( - batchEntry->entries.size() + entries.size()); batchEntry->entries.insert( batchEntry->entries.end(), entries.begin(), entries.end()); return; diff --git a/pxr/imaging/hd/prefixingSceneIndex.cpp b/pxr/imaging/hd/prefixingSceneIndex.cpp index 8a7712b34a..beba7673bf 100644 --- a/pxr/imaging/hd/prefixingSceneIndex.cpp +++ b/pxr/imaging/hd/prefixingSceneIndex.cpp @@ -86,6 +86,65 @@ class Hd_PrefixingSceneIndexPathDataSource // ---------------------------------------------------------------------------- +class Hd_PrefixingSceneIndexPathArrayDataSource + : public HdTypedSampledDataSource> +{ +public: + HD_DECLARE_DATASOURCE(Hd_PrefixingSceneIndexPathArrayDataSource) + + Hd_PrefixingSceneIndexPathArrayDataSource( + const SdfPath &prefix, + HdPathArrayDataSourceHandle inputDataSource) + : _prefix(prefix) + , _inputDataSource(inputDataSource) + { + } + + VtValue GetValue(Time shutterOffset) override + { + return VtValue(GetTypedValue(shutterOffset)); + } + + bool GetContributingSampleTimesForInterval( + Time startTime, Time endTime, + std::vector