Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unnest UV reader and optimize #1915

Merged
merged 1 commit into from
Dec 16, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
271 changes: 166 additions & 105 deletions lib/usd/translators/shading/usdFileTextureWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,8 @@ class PxrUsdTranslators_FileTextureWriter : public UsdMayaShaderWriter
const TfToken& mayaAttrName,
const SdfValueTypeName& typeName) override;

void WriteTransform2dNode(const UsdTimeCode& usdTime, const UsdShadeShader& texShaderSchema);
void WriteTransform2dNode(const UsdTimeCode& usdTime, const UsdShadeShader& texShaderSchema);
SdfPath getPlace2DTexturePath(const MFnDependencyNode& depNodeFn);
};

PXRUSDMAYA_REGISTER_SHADER_WRITER(file, PxrUsdTranslators_FileTextureWriter);
Expand All @@ -77,7 +78,7 @@ TF_DEFINE_PRIVATE_TOKENS(
_tokens,

// UsdPrimvarReader_float2 Prim Name
((PrimvarReaderShaderName, "TexCoordReader"))
((PrimvarReaderShaderName, "shared_TexCoordReader"))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could changing this name affects loading of existing files?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No. The name of the place2dTexture node is not restored when importing (and it was not previously written, as you can see).


// Usd2dTransform Prim Name
((UsdTransform2dShaderName, "UsdTransform2d"))
Expand Down Expand Up @@ -125,49 +126,62 @@ PxrUsdTranslators_FileTextureWriter::PxrUsdTranslators_FileTextureWriter(

// Now create a UsdPrimvarReader shader that the UsdUvTexture shader will
// use.
const SdfPath primvarReaderShaderPath
= texShaderSchema.GetPath().AppendChild(_tokens->PrimvarReaderShaderName);
UsdShadeShader primvarReaderShaderSchema
= UsdShadeShader::Define(GetUsdStage(), primvarReaderShaderPath);

primvarReaderShaderSchema.CreateIdAttr(VtValue(TrUsdTokens->UsdPrimvarReader_float2));

UsdShadeInput varnameInput
= primvarReaderShaderSchema.CreateInput(TrUsdTokens->varname, SdfValueTypeNames->Token);

// We expose the primvar reader varname attribute to the material to allow
// easy specialization based on UV mappings to geometries:
SdfPath materialPath = GetUsdPath().GetParentPath();
UsdShadeMaterial materialSchema(GetUsdStage()->GetPrimAtPath(materialPath));
while (!materialSchema && !materialPath.IsEmpty()) {
materialPath = materialPath.GetParentPath();
materialSchema = UsdShadeMaterial(GetUsdStage()->GetPrimAtPath(materialPath));
}

if (materialSchema) {
TfToken inputName(
TfStringPrintf("%s:%s", depNodeFn.name().asChar(), TrUsdTokens->varname.GetText()));
UsdShadeInput materialInput
= materialSchema.CreateInput(inputName, SdfValueTypeNames->Token);
materialInput.Set(UsdUtilsGetPrimaryUVSetName());
varnameInput.ConnectToSource(materialInput);
// Note: This needs to be done for all nodes that require UV input. In
// the UsdPreviewSurface case, the file node is the only one, but for
// other Maya nodes like cloth, checker, mandelbrot, we will also need
// to resolve the UV channels. This means traversing UV inputs until we
// find the unconnected one that implicitly connects to uvSet[0] of the
// geometry, or an explicit uvChooser node connecting to alternate uvSets.
} else {
varnameInput.Set(UsdUtilsGetPrimaryUVSetName());
}
const SdfPath primvarReaderShaderPath = getPlace2DTexturePath(depNodeFn);

if (!GetUsdStage()->GetPrimAtPath(primvarReaderShaderPath)) {
UsdShadeShader primvarReaderShaderSchema
= UsdShadeShader::Define(GetUsdStage(), primvarReaderShaderPath);

primvarReaderShaderSchema.CreateIdAttr(VtValue(TrUsdTokens->UsdPrimvarReader_float2));

UsdShadeOutput primvarReaderOutput
= primvarReaderShaderSchema.CreateOutput(TrUsdTokens->result, SdfValueTypeNames->Float2);
UsdShadeInput varnameInput
= primvarReaderShaderSchema.CreateInput(TrUsdTokens->varname, SdfValueTypeNames->Token);

// Connect the output of the primvar reader to the texture coordinate
// input of the UV texture.
texShaderSchema.CreateInput(TrUsdTokens->st, SdfValueTypeNames->Float2)
.ConnectToSource(primvarReaderOutput);
// We expose the primvar reader varname attribute to the material to allow
// easy specialization based on UV mappings to geometries:
SdfPath materialPath = GetUsdPath().GetParentPath();
UsdShadeMaterial materialSchema(GetUsdStage()->GetPrimAtPath(materialPath));
while (!materialSchema && !materialPath.IsEmpty()) {
materialPath = materialPath.GetParentPath();
materialSchema = UsdShadeMaterial(GetUsdStage()->GetPrimAtPath(materialPath));
}

if (materialSchema) {
TfToken inputName(
TfStringPrintf("%s:%s", depNodeFn.name().asChar(), TrUsdTokens->varname.GetText()));
UsdShadeInput materialInput
= materialSchema.CreateInput(inputName, SdfValueTypeNames->Token);
materialInput.Set(UsdUtilsGetPrimaryUVSetName());
varnameInput.ConnectToSource(materialInput);
// Note: This needs to be done for all nodes that require UV input. In
// the UsdPreviewSurface case, the file node is the only one, but for
// other Maya nodes like cloth, checker, mandelbrot, we will also need
// to resolve the UV channels. This means traversing UV inputs until we
// find the unconnected one that implicitly connects to uvSet[0] of the
// geometry, or an explicit uvChooser node connecting to alternate uvSets.
} else {
varnameInput.Set(UsdUtilsGetPrimaryUVSetName());
}

UsdShadeOutput primvarReaderOutput = primvarReaderShaderSchema.CreateOutput(
TrUsdTokens->result, SdfValueTypeNames->Float2);

// Connect the output of the primvar reader to the texture coordinate
// input of the UV texture.
texShaderSchema.CreateInput(TrUsdTokens->st, SdfValueTypeNames->Float2)
.ConnectToSource(primvarReaderOutput);
} else {
// Re-using an existing primvar reader:
UsdShadeShader primvarReaderShaderSchema(
GetUsdStage()->GetPrimAtPath(primvarReaderShaderPath));
UsdShadeOutput primvarReaderOutput
= primvarReaderShaderSchema.GetOutput(TrUsdTokens->result);

// Connect the output of the primvar reader to the texture coordinate
// input of the UV texture.
texShaderSchema.CreateInput(TrUsdTokens->st, SdfValueTypeNames->Float2)
.ConnectToSource(primvarReaderOutput);
}
}

/* virtual */
Expand Down Expand Up @@ -503,82 +517,129 @@ void PxrUsdTranslators_FileTextureWriter::WriteTransform2dNode(
}

// Get the TexCoordReader node and its output "result"
const SdfPath primvarReaderShaderPath
= texShaderSchema.GetPath().AppendChild(_tokens->PrimvarReaderShaderName);

const SdfPath primvarReaderShaderPath = getPlace2DTexturePath(depNodeFn);
const UsdShadeShader primvarReaderShader
= texShaderSchema.Get(GetUsdStage(), primvarReaderShaderPath);

const UsdShadeOutput primvarReaderShaderOutput
= primvarReaderShader.GetOutput(TrUsdTokens->result);

// Create the Transform2d node as a child of the UsdUVTexture node
const SdfPath transform2dShaderPath
= texShaderSchema.GetPath().AppendChild(_tokens->UsdTransform2dShaderName);
UsdShadeShader transform2dShaderSchema
= UsdShadeShader::Define(GetUsdStage(), transform2dShaderPath);

transform2dShaderSchema.CreateIdAttr(VtValue(TrUsdTokens->UsdTransform2d));

// Create the Transform2d input "in" attribute and connect it
// to the TexCoordReader output "result"
transform2dShaderSchema.CreateInput(TrUsdTokens->in, SdfValueTypeNames->Float2)
.ConnectToSource(primvarReaderShaderOutput);

// Compute the Transform2d values, converting from Maya's coordinates to USD coordinates

// Maya's place2dtexture transform order seems to be `in * T * S * R`, where the rotation
// pivot is (0.5, 0.5) and scale pivot is (0,0). USD's Transform2d transform order is `in *
// S * R * T`, where the rotation and scale pivots are (0,0). This conversion translates
// from place2dtexture's UV space to Transform2d's UV space: `in * S * T * Rpivot_inverse *
// R * Rpivot`
GfMatrix4f pivotXform = GfMatrix4f().SetTranslate(GfVec3f(0.5, 0.5, 0));
GfMatrix4f translateXform
= GfMatrix4f().SetTranslate(GfVec3f(translationValue[0], translationValue[1], 0));
GfRotation rotation = GfRotation(GfVec3f::ZAxis(), rotationValue);
GfMatrix4f rotationXform = GfMatrix4f().SetRotate(rotation);
GfVec3f scale;
if (fabs(scaleValue[0]) <= std::numeric_limits<float>::epsilon()
|| fabs(scaleValue[1]) <= std::numeric_limits<float>::epsilon()) {
TF_WARN(
"At least one of the components of RepeatUV for %s are set to zero. To avoid divide "
"by zero exceptions, these values are changed to the smallest finite float greater "
"than zero.",
UsdMayaUtil::GetMayaNodeName(GetMayaObject()).c_str());

scale = GfVec3f(
1.0 / std::max(scaleValue[0], std::numeric_limits<float>::min()),
1.0 / std::max(scaleValue[1], std::numeric_limits<float>::min()),
1.0);
// We have two cases. If the node is connected to a place2DTransform, then the transform data
// was on the placement node. If not, then the transform data was on the file node.
std::string usdUvTransformName;
if (primvarReaderShaderPath.GetName() == _tokens->PrimvarReaderShaderName.GetString()) {
usdUvTransformName = TfStringPrintf(
"%s_%s", depNodeFn.name().asChar(), _tokens->UsdTransform2dShaderName.GetText());

} else {
scale = GfVec3f(1.0 / scaleValue[0], 1.0 / scaleValue[1], 1.0);
}
usdUvTransformName = TfStringPrintf(
"%s_%s",
primvarReaderShaderPath.GetName().c_str(),
_tokens->UsdTransform2dShaderName.GetText());
}

const SdfPath transform2dShaderPath = texShaderSchema.GetPath().GetParentPath().AppendChild(
TfToken(usdUvTransformName.c_str()));

if (!GetUsdStage()->GetPrimAtPath(transform2dShaderPath)) {
// Create the Transform2d node as a child of the UsdUVTexture node
UsdShadeShader transform2dShaderSchema
= UsdShadeShader::Define(GetUsdStage(), transform2dShaderPath);

transform2dShaderSchema.CreateIdAttr(VtValue(TrUsdTokens->UsdTransform2d));

// Create the Transform2d input "in" attribute and connect it
// to the TexCoordReader output "result"
transform2dShaderSchema.CreateInput(TrUsdTokens->in, SdfValueTypeNames->Float2)
.ConnectToSource(primvarReaderShaderOutput);

// Compute the Transform2d values, converting from Maya's coordinates to USD coordinates

// Maya's place2dtexture transform order seems to be `in * T * S * R`, where the rotation
// pivot is (0.5, 0.5) and scale pivot is (0,0). USD's Transform2d transform order is `in *
// S * R * T`, where the rotation and scale pivots are (0,0). This conversion translates
// from place2dtexture's UV space to Transform2d's UV space: `in * S * T * Rpivot_inverse *
// R * Rpivot`
GfMatrix4f pivotXform = GfMatrix4f().SetTranslate(GfVec3f(0.5, 0.5, 0));
GfMatrix4f translateXform
= GfMatrix4f().SetTranslate(GfVec3f(translationValue[0], translationValue[1], 0));
GfRotation rotation = GfRotation(GfVec3f::ZAxis(), rotationValue);
GfMatrix4f rotationXform = GfMatrix4f().SetRotate(rotation);
GfVec3f scale;
if (fabs(scaleValue[0]) <= std::numeric_limits<float>::epsilon()
|| fabs(scaleValue[1]) <= std::numeric_limits<float>::epsilon()) {
TF_WARN(
"At least one of the components of RepeatUV for %s are set to zero. To avoid "
"divide "
"by zero exceptions, these values are changed to the smallest finite float greater "
"than zero.",
UsdMayaUtil::GetMayaNodeName(GetMayaObject()).c_str());

scale = GfVec3f(
1.0 / std::max(scaleValue[0], std::numeric_limits<float>::min()),
1.0 / std::max(scaleValue[1], std::numeric_limits<float>::min()),
1.0);
} else {
scale = GfVec3f(1.0 / scaleValue[0], 1.0 / scaleValue[1], 1.0);
}

GfMatrix4f scaleXform = GfMatrix4f().SetScale(scale);

GfMatrix4f scaleXform = GfMatrix4f().SetScale(scale);
GfMatrix4f transform
= scaleXform * translateXform * pivotXform.GetInverse() * rotationXform * pivotXform;
GfVec3f translationResult = transform.ExtractTranslation();
translationValue.Set(translationResult[0], translationResult[1]);

GfMatrix4f transform
= scaleXform * translateXform * pivotXform.GetInverse() * rotationXform * pivotXform;
GfVec3f translationResult = transform.ExtractTranslation();
translationValue.Set(translationResult[0], translationResult[1]);
// Create and set the Transform2d input attributes
transform2dShaderSchema.CreateInput(TrUsdTokens->translation, SdfValueTypeNames->Float2)
.Set(translationValue);

// Create and set the Transform2d input attributes
transform2dShaderSchema.CreateInput(TrUsdTokens->translation, SdfValueTypeNames->Float2)
.Set(translationValue);
transform2dShaderSchema.CreateInput(TrUsdTokens->rotation, SdfValueTypeNames->Float)
.Set(rotationValue);

transform2dShaderSchema.CreateInput(TrUsdTokens->rotation, SdfValueTypeNames->Float)
.Set(rotationValue);
transform2dShaderSchema.CreateInput(TrUsdTokens->scale, SdfValueTypeNames->Float2)
.Set(scaleValue);

transform2dShaderSchema.CreateInput(TrUsdTokens->scale, SdfValueTypeNames->Float2)
.Set(scaleValue);
// Create the Transform2d output "result" attribute
UsdShadeOutput transform2dOutput
= transform2dShaderSchema.CreateOutput(TrUsdTokens->result, SdfValueTypeNames->Float2);

// Get and connect the file texture input "st" to the Transform2d output "result"
UsdShadeInput texShaderSchemaInput = texShaderSchema.GetInput(TrUsdTokens->st);
texShaderSchemaInput.ConnectToSource(transform2dOutput);
} else {
// Re-using an existing transform node:
UsdShadeShader transform2dShaderSchema(GetUsdStage()->GetPrimAtPath(transform2dShaderPath));
UsdShadeOutput transform2dOutput = transform2dShaderSchema.GetOutput(TrUsdTokens->result);
// Get and connect the file texture input "st" to the Transform2d output "result"
UsdShadeInput texShaderSchemaInput = texShaderSchema.GetInput(TrUsdTokens->st);
texShaderSchemaInput.ConnectToSource(transform2dOutput);
}
}

// Create the Transform2d output "result" attribute
UsdShadeOutput transform2dOutput
= transform2dShaderSchema.CreateOutput(TrUsdTokens->result, SdfValueTypeNames->Float2);
SdfPath
PxrUsdTranslators_FileTextureWriter::getPlace2DTexturePath(const MFnDependencyNode& depNodeFn)
{
MStatus status;
std::string usdUvTextureName;
const MPlug plug = depNodeFn.findPlug(
TrMayaTokens->uvCoord.GetText(),
/* wantNetworkedPlug = */ true,
&status);
if (status == MS::kSuccess && plug.isDestination(&status)) {
MPlug source = plug.source(&status);
if (status == MS::kSuccess && !source.isNull()) {
MFnDependencyNode sourceNode(source.node());
usdUvTextureName = sourceNode.name().asChar();
}
}

if (usdUvTextureName.empty()) {
// We want a single UV reader for all file nodes not connected to a place2DTexture node
usdUvTextureName = _tokens->PrimvarReaderShaderName.GetString();
}

// Get and connect the TexCoordReader input "st" to the Transform2d
// output "result"
UsdShadeInput texShaderSchemaInput = texShaderSchema.GetInput(TrUsdTokens->st);
texShaderSchemaInput.ConnectToSource(transform2dOutput);
return GetUsdPath().GetParentPath().AppendChild(TfToken(usdUvTextureName.c_str()));
}

/* virtual */
Expand Down
2 changes: 2 additions & 0 deletions test/lib/mayaUsd/fileio/testShaderWriter.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ def testSimpleShaderWriter(self):
".noiseUV", ".vertexUvOne", ".vertexUvTwo",
".vertexUvThree", ".vertexCameraOne"):
cmds.connectAttr(uv_node + att_name, file_node + att_name, f=True)
cmds.connectAttr(uv_node + ".outUV", file_node + ".uvCoord", f=True)
cmds.connectAttr(uv_node + ".outUvFilterSize", file_node + ".uvFilterSize", f=True)

cmds.connectAttr(file_node + ".outColor",
material_node + ".color", f=True)
Expand Down
Loading