Skip to content

Commit

Permalink
Author arnold ginstances as instanceable usd prims Autodesk#1775
Browse files Browse the repository at this point in the history
  • Loading branch information
sebastienblor committed Mar 13, 2024
1 parent 044ed62 commit 81c8ca4
Show file tree
Hide file tree
Showing 7 changed files with 109 additions and 95 deletions.
1 change: 1 addition & 0 deletions libs/common/constant_strings.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ ASTR2(inputs_file, "inputs:file");
ASTR2(inputs_intensity, "inputs:intensity");
ASTR2(primvars_arnold, "primvars:arnold");
ASTR2(primvars_arnold_light, "primvars:arnold:light");
ASTR2(primvars_arnold_node, "primvars:arnold:node");
ASTR2(log_file, "log:file");
ASTR2(log_verbosity, "log:verbosity");
ASTR2(primvars_arnold_subdiv_type, "primvars:arnold:subdiv_type");
Expand Down
8 changes: 7 additions & 1 deletion libs/translator/reader/reader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -635,8 +635,14 @@ void UsdArnoldReader::ReadPrimitive(const UsdPrim &prim, UsdArnoldReaderContext
// therefore be created by a single thread in ProcessConnection. Given that this prim
// is a prototype, it will be created as a nested usd procedural with object path set
// to the protoype prim's name. This will support instances of hierarchies.
context.AddConnection(
// Note that if this primitive has an attributes primvars:arnold:node, it means that it was
// created from an Arnold ginstance node, in which case we don't want to check the prototype
// path (which will always trigger a nested proc creation) #1775
UsdAttribute nodeAttr = prim.GetAttribute(str::t_primvars_arnold_node);
if (!nodeAttr || !nodeAttr.HasAuthoredValue()) {
context.AddConnection(
ginstance, "node", proto.GetPath().GetText(), ArnoldAPIAdapter::CONNECTION_PTR);
}
return;
}
}
Expand Down
5 changes: 2 additions & 3 deletions libs/translator/writer/registry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ UsdArnoldWriterRegistry::UsdArnoldWriterRegistry(bool writeBuiltin)
RegisterWriter("driver_deepexr", new UsdArnoldWriteDriver());
RegisterWriter("driver_jpeg", new UsdArnoldWriteDriver());
RegisterWriter("driver_png", new UsdArnoldWriteDriver());

RegisterWriter("ginstance", new UsdArnoldWriteGinstance());
}

// Now let's iterate over all the arnold classes known at this point
Expand All @@ -79,9 +81,6 @@ UsdArnoldWriterRegistry::UsdArnoldWriterRegistry(bool writeBuiltin)
universeCreated = true;
}

// Register a writer for ginstance, whose behaviour is a
// bit special regarding default values
RegisterWriter("ginstance", new UsdArnoldWriteGinstance());

// Iterate over all node types.
// We are skipping filter nodes because they're translated through the options writer
Expand Down
71 changes: 0 additions & 71 deletions libs/translator/writer/write_arnold_type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -117,74 +117,3 @@ void UsdArnoldWriteArnoldType::Write(const AtNode *node, UsdArnoldWriter &writer
}
_WriteArnoldParameters(node, writer, prim, "arnold");
}

void UsdArnoldWriteGinstance::_ProcessInstanceAttribute(
UsdPrim &prim, const AtNode *node, const AtNode *target,
const char *attrName, int attrType, UsdArnoldWriter &writer)
{
if (AiNodeEntryLookUpParameter(AiNodeGetNodeEntry(target), AtString(attrName)) == nullptr)
return; // the attribute doesn't exist in the instanced node

// Now compare the values between the ginstance and the target node. If the value
// is different we'll want to write it even though it's the default value
bool writeValue = false;
SdfValueTypeName usdType;
if (attrType == AI_TYPE_BOOLEAN) {
writeValue = (AiNodeGetBool(node, AtString(attrName)) != AiNodeGetBool(target, AtString(attrName)));
usdType = SdfValueTypeNames->Bool;
} else if (attrType == AI_TYPE_BYTE) {
writeValue = (AiNodeGetByte(node, AtString(attrName)) != AiNodeGetByte(target, AtString(attrName)));
usdType = SdfValueTypeNames->UChar;
} else
return;

if (writeValue) {
std::string namespacedAttr = std::string("arnold:") + std::string(attrName);
UsdAttribute attr = prim.CreateAttribute(TfToken(namespacedAttr.c_str()), usdType, false);
if (attrType == AI_TYPE_BOOLEAN)
writer.SetAttribute(attr, AiNodeGetBool(node, AtString(attrName)));
else if (attrType == AI_TYPE_BYTE)
writer.SetAttribute(attr, AiNodeGetByte(node, AtString(attrName)));
}
_exportedAttrs.insert(attrName);
}

void UsdArnoldWriteGinstance::Write(const AtNode *node, UsdArnoldWriter &writer)
{
// get the output name of this USD primitive
std::string nodeName = GetArnoldNodeName(node, writer);
UsdStageRefPtr stage = writer.GetUsdStage(); // get the current stage defined in the writer
SdfPath objPath(nodeName);

writer.CreateHierarchy(objPath);
UsdPrim prim = stage->DefinePrim(objPath, TfToken(_usdName));

AtNode *target = (AtNode *)AiNodeGetPtr(node, AtString("node"));
if (target) {
_ProcessInstanceAttribute(prim, node, target, "visibility", AI_TYPE_BYTE, writer);
_ProcessInstanceAttribute(prim, node, target, "sidedness", AI_TYPE_BYTE, writer);
_ProcessInstanceAttribute(prim, node, target, "matte", AI_TYPE_BOOLEAN, writer);
_ProcessInstanceAttribute(prim, node, target, "receive_shadows", AI_TYPE_BOOLEAN, writer);
_ProcessInstanceAttribute(prim, node, target, "invert_normals", AI_TYPE_BOOLEAN, writer);
_ProcessInstanceAttribute(prim, node, target, "self_shadows", AI_TYPE_BOOLEAN, writer);

writer.WritePrimitive(target);
std::string targetName = GetArnoldNodeName(target, writer);
SdfPath targetPath(targetName);
UsdPrim targetPrim = stage->GetPrimAtPath(targetPath);
UsdGeomBoundable targetBoundable(targetPrim);
UsdAttribute extentsAttr = targetBoundable.GetExtentAttr();
if (extentsAttr) {
VtVec3fArray extents;
extentsAttr.Get(&extents, (float)writer.GetTime().GetValue());

UsdGeomBoundable boundable(prim);
writer.SetAttribute(boundable.CreateExtentAttr(), extents);
}
}
UsdGeomXformable xformable(prim);
_WriteMatrix(xformable, node, writer);
_WriteMaterialBinding(node, prim, writer);

_WriteArnoldParameters(node, writer, prim, "arnold");
}
20 changes: 0 additions & 20 deletions libs/translator/writer/write_arnold_type.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,23 +52,3 @@ class UsdArnoldWriteArnoldType : public UsdArnoldPrimWriter {
std::string _entryTypeName;
};

/**
* Ginstance nodes require a special treatment, because of the behaviour of
* default values. In general we can skip authoring an attribute if the value
* is different from default, but that's not the case for instances. Here,
* we'll compare the value of the attribute with the corresponding value
* for the instanced node. If it is different we will write it, even if the
* value is equal to default
**/
class UsdArnoldWriteGinstance : public UsdArnoldWriteArnoldType {
public:
UsdArnoldWriteGinstance() : UsdArnoldWriteArnoldType("ginstance", "ArnoldGinstance", "shape") {}

virtual ~UsdArnoldWriteGinstance() {}

protected:
void Write(const AtNode *node, UsdArnoldWriter &writer) override;

void _ProcessInstanceAttribute(
UsdPrim &prim, const AtNode *node, const AtNode *target, const char *attrName, int attrType, UsdArnoldWriter &writer);
};
80 changes: 80 additions & 0 deletions libs/translator/writer/write_geometry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
// limitations under the License.
#include "write_geometry.h"

#include <constant_strings.h>
#include <ai.h>
#include <cstdio>
#include <cstring>
Expand All @@ -26,12 +27,17 @@
#include <pxr/usd/usd/prim.h>
#include <pxr/usd/usdGeom/basisCurves.h>
#include <pxr/usd/usdGeom/mesh.h>
#include <pxr/usd/usdGeom/xform.h>
#include <pxr/usd/usdGeom/points.h>
#include <pxr/usd/usdGeom/primvarsAPI.h>

//-*************************************************************************

PXR_NAMESPACE_USING_DIRECTIVE
TF_DEFINE_PRIVATE_TOKENS(
_tokens,
((inherit_xform, "primvars:arnold:inherit_xform"))
);

void UsdArnoldWriteMesh::Write(const AtNode *node, UsdArnoldWriter &writer)
{
Expand Down Expand Up @@ -353,3 +359,77 @@ void UsdArnoldWriteProceduralCustom::Write(const AtNode *node, UsdArnoldWriter &
AiParamValueMapDestroy(params);
AiUniverseDestroy(universe);
}

void UsdArnoldWriteGinstance::_ProcessInstanceAttribute(
UsdPrim &prim, const AtNode *node, const AtNode *target,
const char *attrName, int attrType, UsdArnoldWriter &writer)
{
if (AiNodeEntryLookUpParameter(AiNodeGetNodeEntry(target), AtString(attrName)) == nullptr)
return; // the attribute doesn't exist in the instanced node

// Now compare the values between the ginstance and the target node. If the value
// is different we'll want to write it even though it's the default value
bool writeValue = false;
SdfValueTypeName usdType;
if (attrType == AI_TYPE_BOOLEAN) {
writeValue = (AiNodeGetBool(node, AtString(attrName)) != AiNodeGetBool(target, AtString(attrName)));
usdType = SdfValueTypeNames->Bool;
} else if (attrType == AI_TYPE_BYTE) {
writeValue = (AiNodeGetByte(node, AtString(attrName)) != AiNodeGetByte(target, AtString(attrName)));
usdType = SdfValueTypeNames->UChar;
} else
return;

if (writeValue) {
std::string namespacedAttr = std::string("primvars:arnold:") + std::string(attrName);
UsdAttribute attr = prim.CreateAttribute(TfToken(namespacedAttr.c_str()), usdType, false);
if (attrType == AI_TYPE_BOOLEAN)
writer.SetAttribute(attr, AiNodeGetBool(node, AtString(attrName)));
else if (attrType == AI_TYPE_BYTE)
writer.SetAttribute(attr, AiNodeGetByte(node, AtString(attrName)));
}
_exportedAttrs.insert(attrName);
}

void UsdArnoldWriteGinstance::Write(const AtNode *node, UsdArnoldWriter &writer)
{
AtNode *target = (AtNode*)AiNodeGetPtr(node, str::node);
if (target == nullptr)
return;

writer.WritePrimitive(target);
std::string targetName = UsdArnoldPrimWriter::GetArnoldNodeName(target, writer);
SdfPath targetPath(targetName);
UsdStageRefPtr stage = writer.GetUsdStage(); // get the current stage defined in the writer
UsdPrim targetPrim = stage->GetPrimAtPath(targetPath);
TfToken targetPrimType;
if (targetPrim)
targetPrimType = targetPrim.GetTypeName();

// get the output name of this USD primitive
std::string nodeName = GetArnoldNodeName(node, writer);
SdfPath objPath(nodeName);
writer.CreateHierarchy(objPath);
UsdPrim prim = stage->DefinePrim(objPath, targetPrimType);
prim.SetInstanceable(true);
prim.GetReferences().AddInternalReference(targetPath);

_ProcessInstanceAttribute(prim, node, target, "visibility", AI_TYPE_BYTE, writer);
_ProcessInstanceAttribute(prim, node, target, "sidedness", AI_TYPE_BYTE, writer);
_ProcessInstanceAttribute(prim, node, target, "matte", AI_TYPE_BOOLEAN, writer);
_ProcessInstanceAttribute(prim, node, target, "receive_shadows", AI_TYPE_BOOLEAN, writer);
_ProcessInstanceAttribute(prim, node, target, "invert_normals", AI_TYPE_BOOLEAN, writer);
_ProcessInstanceAttribute(prim, node, target, "self_shadows", AI_TYPE_BOOLEAN, writer);

// Ensure inherit_xform is always authored, even if its value is left to default
UsdAttribute inheritXformAttr = prim.CreateAttribute(
_tokens->inherit_xform, SdfValueTypeNames->Bool, false);
writer.SetAttribute(inheritXformAttr, AiNodeGetBool(node, str::inherit_xform));
_exportedAttrs.insert("inherit_xform");

if (prim.IsA<UsdGeomXformable>()) {
UsdGeomXformable xformable(prim);
_WriteMatrix(xformable, node, writer);
}
_WriteArnoldParameters(node, writer, prim, "primvars:arnold");
}
19 changes: 19 additions & 0 deletions libs/translator/writer/write_geometry.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,22 @@ class UsdArnoldWriteProceduralCustom : public UsdArnoldPrimWriter {
private:
std::string _nodeEntry;
};

/**
* Ginstance nodes require a special treatment, because of the behaviour of
* default values. In general we can skip authoring an attribute if the value
* is different from default, but that's not the case for instances. Here,
* we'll compare the value of the attribute with the corresponding value
* for the instanced node. If it is different we will write it, even if the
* value is equal to default
**/
class UsdArnoldWriteGinstance : public UsdArnoldPrimWriter {
public:
UsdArnoldWriteGinstance() : UsdArnoldPrimWriter() {}

void Write(const AtNode *node, UsdArnoldWriter &writer) override;

void _ProcessInstanceAttribute(
UsdPrim &prim, const AtNode *node, const AtNode *target, const char *attrName, int attrType, UsdArnoldWriter &writer);

};

0 comments on commit 81c8ca4

Please sign in to comment.