Skip to content

Commit

Permalink
Merge pull request #50 from SebSchmidtWetaFx/UsdTriImagingHydra2
Browse files Browse the repository at this point in the history
Adding Hydra2 PrimAdapter for triangle schema
  • Loading branch information
SebSchmidtWetaFx authored Jul 31, 2024
2 parents 6ea28a1 + 9d93762 commit aad3a75
Show file tree
Hide file tree
Showing 11 changed files with 542 additions and 4 deletions.
15 changes: 12 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@

A collection of example plugins for [Pixar's USD](https://github.com/PixarAnimationStudios/USD) (Universal Scene Description).

This project also aims to provide a set of CMake utilities for building USD plugins outside of the USD project source tree. The utilities are heavily based on the build logic prescribed by the USD project itself.
This project also aims to provide a set of CMake utilities for building USD plugins outside of the USD project source tree. The utilities are heavily based on the build logic prescribed by the USD project itself.

We hope the minimal examples and surrounding build infrastructure can be useful to USD community developers interested in building and deploying their own plugin(s).
We hope the minimal examples and surrounding build infrastructure can be useful to USD community developers interested in building and deploying their own plugin(s).

Huge thanks to Pixar's USD team for providing a highly extensible platform!

Expand All @@ -22,11 +22,14 @@ Huge thanks to Pixar's USD team for providing a highly extensible platform!

**USDPluginExamples** provides the following USD plugins:
- [usdTri](./src/usdTri): A schema library defining a **Triangle** prim type.
- [usdTriImaging](./src/usdTriImaging): A prim adapter which images the **Triangle** prim type.
- [usdTriImaging](./src/usdTriImaging): A *Hydra 1 only* prim adapter which images the **Triangle** prim type. [*]
- [usdTriImagingHd2](./src/usdTriImagingHd2): A *Hydra 2 only* prim adapter which images the **Triangle** prim type. [*]
- [usdTriFileFormat](./src/usdTriFileFormat): A file format plugin which authors a triangular mesh for a `.triangle` payload.
- [hdTri](./src/hdTri): A hydra renderer plugin which images a triangle (in the most direct sense).
- [usdviewTri](./src/usdviewTri): An usdview plugin providing a menu command to define child Triangle prim(s) under selected paths.

[*] We deliberatly split the Hydra 1 & 2 Triangle Prim Adapters into two plugins/sources to outline the differences

There are many other USD plugins available online - check out [USD Working Group: Projects & Resources](https://wiki.aswf.io/display/WGUSD/USD+Projects+and+Resources) for more!

## Dependencies
Expand Down Expand Up @@ -75,6 +78,7 @@ CMake options for configuring this project:
| `BOOST_ROOT` | Root directory of Boost installation | |
| `ENABLE_PYTHON_SUPPORT` | Enable python support. Must match python support of USD installation. | `ON` |
| `BUILD_TESTING` | Enable automated testing. | `ON` |
| `BUILD_HYDRA2` | Enable building Hydra2 plugins, will disable Hydra1 plugin building. | `OFF` |

## Running

Expand All @@ -98,6 +102,11 @@ Additionally, Linux requires:
<sub>Note: libraries and plugins are installed into different locations - thus PXR_PLUGINPATH_NAME specifies
two separate values.</sub>

To run the Hydra2 Prim Adapter with Storm/GL you have to enable the SceneIndex for UsdImagingGL
| Environment Variable | Value(s) |
| ---------------------------------------- | --------------------------------------------------------------------- |
| `USDIMAGINGGL_ENGINE_ENABLE_SCENE_INDEX` | `1`````````````````` |


Once the environment variables have been set-up, an example scene in this repo can be previewed with **usdview**:
```
Expand Down
1 change: 1 addition & 0 deletions cmake/Options.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@

option(ENABLE_PYTHON_SUPPORT "Enable support for python." ON)
option(BUILD_TESTING "Build & execute tests as part of build" ON)
option(BUILD_HYDRA2 "Build Hydra2 Plugins, disables Hydra1 plugins" OFF)
7 changes: 6 additions & 1 deletion src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,12 @@ add_subdirectory(usdTri)
add_subdirectory(usdviewTri)

# Build & install plugins.
add_subdirectory(usdTriImaging)
if (BUILD_HYDRA2)
add_subdirectory(usdTriImagingHd2)
else()
add_subdirectory(usdTriImaging)
endif()

add_subdirectory(usdTriFileFormat)
add_subdirectory(hdTri)

Expand Down
24 changes: 24 additions & 0 deletions src/usdTriImagingHd2/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
usd_plugin(usdTriImagingHd2

CPPFILES
triangleAdapter.cpp
debugCodes.cpp
dataSourceTri.cpp

LIBRARIES
arch
js
plug
usd
tf
sdf
vt
gf
hd
usdGeom
usdImaging
usdTri

RESOURCE_FILES
plugInfo.json
)
241 changes: 241 additions & 0 deletions src/usdTriImagingHd2/dataSourceTri.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,241 @@
//
// Copyright © 2024 Weta FX Limited
//
// SPDX-License-Identifier: Apache-2.0
//

#include "dataSourceTri.h"
#include "debugCodes.h"

#include <usdpluginexamples/usdTri/tokens.h>
#include <usdpluginexamples/usdTri/triangle.h>

#include "pxr/imaging/hd/meshSchema.h"
#include <pxr/imaging/hd/meshTopologySchema.h>
#include <pxr/imaging/hd/primvarsSchema.h>
#include <pxr/imaging/hd/retainedDataSource.h>
#include <pxr/imaging/hd/overlayContainerDataSource.h>
#include <pxr/usdImaging/usdImaging/dataSourceMesh.h>

PXR_NAMESPACE_OPEN_SCOPE

// a static container-handle for the mesh-topology
HdContainerDataSourceHandle
_meshTopologyDs()
{
static const VtIntArray faceVertexCounts = { 3 };

static const VtIntArray faceVertexIndices = { 0, 1, 2 };

using _IntArrayDs = HdRetainedTypedSampledDataSource<VtIntArray>;

static const _IntArrayDs::Handle fvcDs = _IntArrayDs::New(faceVertexCounts);

static const _IntArrayDs::Handle fviDs =
_IntArrayDs::New(faceVertexIndices);

static const HdContainerDataSourceHandle meshDs =
HdMeshSchema::Builder()
.SetTopology(HdMeshTopologySchema::Builder()
.SetFaceVertexCounts(fvcDs)
.SetFaceVertexIndices(fviDs)
.Build())
.Build();

return meshDs;
}

// a DataHandle producing Sampled Vec3fArray data
// to be used as points of the triangle
// it carries a great resemblence and is very much a copy of
// UsdImagingDataSourceAttribute we technically would just need to overwrite
// ::GetTypedValue(...) for our purposes here, but the constructor of
// UsdImagingDataSourceAttribute is private so we need to re-implement
// everything

class _PointsFromSideLengthDataSource
: public HdTypedSampledDataSource<VtVec3fArray>
{
public:
HD_DECLARE_DATASOURCE(_PointsFromSideLengthDataSource);

// copied the code from UsdImagingDataSourceAttribute
bool GetContributingSampleTimesForInterval(
Time startTime,
Time endTime,
std::vector<Time>* outSampleTimes) override
{
UsdTimeCode time = _stageGlobals.GetTime();
if (!_valueQuery.ValueMightBeTimeVarying() || !time.IsNumeric()) {
return false;
}

GfInterval interval(time.GetValue() + startTime,
time.GetValue() + endTime);
std::vector<double> timeSamples;
_valueQuery.GetTimeSamplesInInterval(interval, &timeSamples);

// Add boundary timesamples, if necessary.
if (timeSamples.empty() || timeSamples[0] > interval.GetMin()) {
timeSamples.insert(timeSamples.begin(), interval.GetMin());
}
if (timeSamples.back() < interval.GetMax()) {
timeSamples.push_back(interval.GetMax());
}

// We need to convert the time array because usd uses double and
// hydra (and prman) use float :/.
outSampleTimes->resize(timeSamples.size());
for (size_t i = 0; i < timeSamples.size(); ++i) {
(*outSampleTimes)[i] = timeSamples[i] - time.GetValue();
}

return true;
}

// copied the code from UsdImagingDataSourceAttribute
VtValue GetValue(Time shutterOffset) override
{
return VtValue(GetTypedValue(shutterOffset));
}

// here we are calculating the points of the triangle based on the values
// of our sideLength attribute
VtArray<GfVec3f> GetTypedValue(Time shutterOffset) override
{

double sideLength = 1.0f;
UsdTimeCode time = _stageGlobals.GetTime();
if (time.IsNumeric()) {
time = UsdTimeCode(time.GetValue() + shutterOffset);
}
_valueQuery.Get<double>(&sideLength, time);

VtVec3fArray points{
GfVec3f(0.0f, 0.57735027f * sideLength, 0.0f),
GfVec3f(-0.5f * sideLength, -0.28867513f * sideLength, 0.0f),
GfVec3f(0.5f * sideLength, -0.28867513f * sideLength, 0.0f)
};
return points;
}

private:
// constructor, which also registeres the points-primvar to be 'time'
// dependend if our sideLength attribute might be timeVarying
_PointsFromSideLengthDataSource(
const SdfPath& sceneIndexPath,
const UsdImagingDataSourceStageGlobals& stageGlobals,
UsdPrim myPrim)
: _stageGlobals(stageGlobals)
, _valueQuery(myPrim, UsdTriTokens->sideLength)
{
if (_valueQuery.ValueMightBeTimeVarying()) {
_stageGlobals.FlagAsTimeVarying(
sceneIndexPath, HdPrimvarsSchema::GetPointsLocator());
}
}

const UsdImagingDataSourceStageGlobals& _stageGlobals;
UsdAttributeQuery _valueQuery;
};
HD_DECLARE_DATASOURCE_HANDLES(_PointsFromSideLengthDataSource);

/// ---------------------------------------------------------------------
/// ---------------------------------------------------------------------

UsdImagingDataSourceTriPrim::UsdImagingDataSourceTriPrim(
const SdfPath& sceneIndexPath,
UsdPrim usdPrim,
const UsdImagingDataSourceStageGlobals& stageGlobals)
: UsdImagingDataSourceGprim(sceneIndexPath, usdPrim, stageGlobals)
{
// Note: DataSourceGprim handles the special PointBased primvars for us.
}

TfTokenVector
UsdImagingDataSourceTriPrim::GetNames()
{
// using the GPrim - Names
TfTokenVector result = UsdImagingDataSourceGprim::GetNames();

// and adding what our specialisation of the GPrim DataSource is providing
// on top
// ... We do provide a Mesh
result.push_back(HdMeshSchemaTokens->mesh);

// and we're technically also providing primvars
// however as the GPrimDataSource already does this we dont
// need to add it here
// result.push_back(HdPrimvarsSchemaTokens->primvars);

return result;
}

HdDataSourceBaseHandle
UsdImagingDataSourceTriPrim::Get(const TfToken& i_name)
{
// providing the data-source for the Mesh
if (i_name == HdMeshSchemaTokens->mesh) {
// nothing magic here just a mesh with a static topology
return _meshTopologyDs();
}

// for other requests, we're forwarding them to the
// DataSourceGPrim
HdDataSourceBaseHandle result = UsdImagingDataSourceGprim::Get(i_name);

// while the base GPrimDataSource does deal with primvars in
// general it does not deal with points-primvars
// so we are overlaying our Points DataSource
if (i_name == HdPrimvarsSchemaTokens->primvars) {
// here it is a bit more tricky
// we overlaying the Primvars - DataHandle that we get from our
// BaseClass with our own primvars containing the calculated points
return HdOverlayContainerDataSource::New(
// the Primvar data-source from the GPrim
HdContainerDataSource::Cast(result),
// and overlaying our own
HdRetainedContainerDataSource::New(
HdPrimvarsSchemaTokens->points,
HdPrimvarSchema::Builder()
.SetPrimvarValue(
// with our DataSource for the Points calculated from
// our sideLength attribute
_PointsFromSideLengthDataSource::New(
_GetSceneIndexPath(),
_GetStageGlobals(),
_GetUsdPrim()))
.SetInterpolation(
HdPrimvarSchema::BuildInterpolationDataSource(
HdPrimvarSchemaTokens->vertex))
.SetRole(HdPrimvarSchema::BuildRoleDataSource(
HdPrimvarSchemaTokens->point))
.Build()));
}
return result;
}

/*static*/
HdDataSourceLocatorSet
UsdImagingDataSourceTriPrim::Invalidate(
UsdPrim const& prim,
const TfToken& subprim,
const TfTokenVector& properties,
const UsdImagingPropertyInvalidationType invalidationType)
{
HdDataSourceLocatorSet locators;
for (const TfToken& propertyName : properties) {
// if the sideLength property got changed we wanna
// invalidate/dirty the points primvar, so it gets updated
if (propertyName == UsdTriTokens->sideLength) {
locators.insert(HdPrimvarsSchema::GetPointsLocator());
}
}

// Give base classes a chance to invalidate.
locators.insert(UsdImagingDataSourceGprim::Invalidate(
prim, subprim, properties, invalidationType));
return locators;
}

PXR_NAMESPACE_CLOSE_SCOPE
49 changes: 49 additions & 0 deletions src/usdTriImagingHd2/dataSourceTri.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
//
// Copyright © 2024 Weta FX Limited
//
// SPDX-License-Identifier: Apache-2.0
//
#pragma once

#include "pxr/usdImaging/usdImaging/dataSourceGprim.h"

PXR_NAMESPACE_OPEN_SCOPE

///
/// \brief The UsdImagingDataSourceTriPrim class - inheriting from a GPrim
/// DataSource as we can use a lot of the existing infrastructure of GPrims e.g.
/// support for Transforms, Primvars etc.
/// We do override the implementations for 'Points' & 'Topology' to return the
/// triangle relevant data. An other approach could be to override
/// the UsdImagingGprimAdapter::GetPoints() /
/// UsdImagingGprimAdapter::GetTopology functions
///
class UsdImagingDataSourceTriPrim : public UsdImagingDataSourceGprim
{
public:
HD_DECLARE_DATASOURCE(UsdImagingDataSourceTriPrim);

// return the names of the data-sources this object can provide
TfTokenVector GetNames() override;

// return the data-sources based on the names
HdDataSourceBaseHandle Get(const TfToken& i_name) override;

// callbacks to help which DataSource Locations need to be invalidated
// based on the prim/subPrim/properties
static HdDataSourceLocatorSet Invalidate(
UsdPrim const& prim,
const TfToken& subprim,
const TfTokenVector& properties,
UsdImagingPropertyInvalidationType invalidationType);

private:
UsdImagingDataSourceTriPrim(
const SdfPath& sceneIndexPath,
UsdPrim usdPrim,
const UsdImagingDataSourceStageGlobals& stageGlobals);
};

HD_DECLARE_DATASOURCE_HANDLES(UsdImagingDataSourceTriPrim);

PXR_NAMESPACE_CLOSE_SCOPE
Loading

0 comments on commit aad3a75

Please sign in to comment.