diff --git a/pxr/usdValidation/usdGeomValidators/plugInfo.json b/pxr/usdValidation/usdGeomValidators/plugInfo.json index 4ba1af11b9..1c2e6938ce 100644 --- a/pxr/usdValidation/usdGeomValidators/plugInfo.json +++ b/pxr/usdValidation/usdGeomValidators/plugInfo.json @@ -23,7 +23,10 @@ "schemaTypes": [ "UsdGeomSubset" ] - }, + }, + "YUpAxisValidator": { + "doc": "UsdStage metadata of upAxis must be Y." + }, "keywords": [ "UsdGeomValidators" ] diff --git a/pxr/usdValidation/usdGeomValidators/testenv/testUsdGeomValidators.cpp b/pxr/usdValidation/usdGeomValidators/testenv/testUsdGeomValidators.cpp index 49160f81fc..4fd3cea47b 100644 --- a/pxr/usdValidation/usdGeomValidators/testenv/testUsdGeomValidators.cpp +++ b/pxr/usdValidation/usdGeomValidators/testenv/testUsdGeomValidators.cpp @@ -29,6 +29,7 @@ TestUsdGeomValidators() UsdGeomValidatorNameTokens->subsetFamilies, UsdGeomValidatorNameTokens->subsetParentIsImageable, UsdGeomValidatorNameTokens->stageMetadataChecker, + UsdGeomValidatorNameTokens->yUpAxisValidator }; const UsdValidationRegistry ®istry @@ -42,7 +43,7 @@ TestUsdGeomValidators() UsdValidationValidatorMetadataVector metadata = registry.GetValidatorMetadataForPlugin( _tokens->usdGeomValidatorsPlugin); - TF_AXIOM(metadata.size() == 3); + TF_AXIOM(metadata.size() == 4); for (const UsdValidationValidatorMetadata &metadata : metadata) { validatorMetadataNameSet.insert(metadata.name); } @@ -315,7 +316,7 @@ TestUsdGeomSubsetParentIsImageable() } static void -TestUsdStageMetadata() +TestUsdGeomStageMetadata() { // Get stageMetadataChecker UsdValidationRegistry ®istry = UsdValidationRegistry::GetInstance(); @@ -333,16 +334,17 @@ TestUsdStageMetadata() TF_AXIOM(errors.size() == 2); auto rootLayerIdentifier = rootLayer->GetIdentifier().c_str(); const std::vector expectedErrorMessages = { - TfStringPrintf("Stage with root layer <%s> does not specify its linear " - "scale in metersPerUnit.", + TfStringPrintf("Stage with root layer <%s> does not specify its " + "linear scale in metersPerUnit.", rootLayerIdentifier), - TfStringPrintf("Stage with root layer <%s> does not specify an upAxis.", + TfStringPrintf("Stage with root layer <%s> does not specify an " + "upAxis.", rootLayerIdentifier) }; const std::vector expectedErrorIdentifiers = { TfToken( - "usdGeomValidators:StageMetadataChecker.MissingMetersPerUnitMetadata"), + "usdGeomValidators:StageMetadataChecker.MissingMetersPerUnitMetadata"), TfToken("usdGeomValidators:StageMetadataChecker.MissingUpAxisMetadata") }; @@ -364,14 +366,51 @@ TestUsdStageMetadata() TF_AXIOM(errors.empty()); } +static void +TestUsdGeomYUpAxisValidator() +{ + UsdValidationRegistry ®istry = UsdValidationRegistry::GetInstance(); + const UsdValidationValidator *validator = registry.GetOrLoadValidatorByName( + UsdGeomValidatorNameTokens->yUpAxisValidator); + TF_AXIOM(validator); + + // Create an empty stage with a Z up axis + SdfLayerRefPtr rootLayer = SdfLayer::CreateAnonymous(); + UsdStageRefPtr usdStage = UsdStage::Open(rootLayer); + + UsdGeomSetStageUpAxis(usdStage, UsdGeomTokens->z); + + UsdValidationErrorVector errors = validator->Validate(usdStage); + + // Verify the error is present + const TfToken expectedIdentifier = + TfToken("usdGeomValidators:YUpAxisValidator.nonYUpAxis"); + const std::string expectedErrorMsg = + "Stage specifies upAxis 'Z'. upAxis should be 'Y'."; + TF_AXIOM(errors.size() == 1); + TF_AXIOM(errors[0].GetType() == UsdValidationErrorType::Error); + TF_AXIOM(errors[0].GetIdentifier() == expectedIdentifier); + TF_AXIOM(errors[0].GetSites().size() == 1); + TF_AXIOM(errors[0].GetSites()[0].IsValid()); + TF_AXIOM(errors[0].GetMessage() == expectedErrorMsg); + + // Change the up axis to Y + UsdGeomSetStageUpAxis(usdStage, UsdGeomTokens->y); + + errors = validator->Validate(usdStage); + + // Verify the errors are fixed + TF_AXIOM(errors.empty()); +} + int main() { TestUsdGeomValidators(); TestUsdGeomSubsetFamilies(); TestUsdGeomSubsetParentIsImageable(); - TestUsdStageMetadata(); + TestUsdGeomStageMetadata(); + TestUsdGeomYUpAxisValidator(); - std::cout << "OK\n"; return EXIT_SUCCESS; } diff --git a/pxr/usdValidation/usdGeomValidators/validatorTokens.h b/pxr/usdValidation/usdGeomValidators/validatorTokens.h index 5340d6a8b1..8c84fdf8b1 100644 --- a/pxr/usdValidation/usdGeomValidators/validatorTokens.h +++ b/pxr/usdValidation/usdGeomValidators/validatorTokens.h @@ -19,7 +19,8 @@ PXR_NAMESPACE_OPEN_SCOPE #define USD_GEOM_VALIDATOR_NAME_TOKENS \ ((stageMetadataChecker, "usdGeomValidators:StageMetadataChecker")) \ ((subsetFamilies, "usdGeomValidators:SubsetFamilies")) \ - ((subsetParentIsImageable, "usdGeomValidators:SubsetParentIsImageable")) + ((subsetParentIsImageable, "usdGeomValidators:SubsetParentIsImageable")) \ + ((yUpAxisValidator, "usdGeomValidators:YUpAxisValidator")) #define USD_GEOM_VALIDATOR_KEYWORD_TOKENS \ (UsdGeomSubset) \ @@ -29,7 +30,8 @@ PXR_NAMESPACE_OPEN_SCOPE ((missingMetersPerUnitMetadata, "MissingMetersPerUnitMetadata")) \ ((missingUpAxisMetadata, "MissingUpAxisMetadata")) \ ((invalidSubsetFamily, "InvalidSubsetFamily")) \ - ((notImageableSubsetParent, "NotImageableSubsetParent")) + ((notImageableSubsetParent, "NotImageableSubsetParent")) \ + ((nonYUpAxis, "nonYUpAxis")) /// \def USD_GEOM_VALIDATOR_NAME_TOKENS /// Tokens representing validator names. Note that for plugin provided diff --git a/pxr/usdValidation/usdGeomValidators/validators.cpp b/pxr/usdValidation/usdGeomValidators/validators.cpp index 08f0d2171f..618a7fca1f 100644 --- a/pxr/usdValidation/usdGeomValidators/validators.cpp +++ b/pxr/usdValidation/usdGeomValidators/validators.cpp @@ -142,6 +142,34 @@ _SubsetParentIsImageable(const UsdPrim &usdPrim) usdPrim.GetPath().GetText(), parentPrim.GetPath().GetText())) }; } +static UsdValidationErrorVector +_YUpAxisValidator(const UsdStagePtr &usdStage) +{ + if (usdStage->HasAuthoredMetadata(UsdGeomTokens->upAxis)) { + TfToken axis; + usdStage->GetMetadata(UsdGeomTokens->upAxis, &axis); + + if (axis != UsdGeomTokens->y) + { + return { + UsdValidationError( + UsdGeomValidationErrorNameTokens->nonYUpAxis, + UsdValidationErrorType::Error, + UsdValidationErrorSites{UsdValidationErrorSite(usdStage, + SdfPath("/"))}, + TfStringPrintf( + "Stage specifies upAxis '%s'. upAxis should" + " be 'Y'.", axis.GetText()) + ) + }; + } + } + + return {}; +} + + + TF_REGISTRY_FUNCTION(UsdValidationRegistry) { UsdValidationRegistry ®istry = UsdValidationRegistry::GetInstance(); @@ -156,6 +184,10 @@ TF_REGISTRY_FUNCTION(UsdValidationRegistry) registry.RegisterPluginValidator( UsdGeomValidatorNameTokens->subsetParentIsImageable, _SubsetParentIsImageable); + + registry.RegisterPluginValidator( + UsdGeomValidatorNameTokens->yUpAxisValidator, + _YUpAxisValidator); } PXR_NAMESPACE_CLOSE_SCOPE