1 //
2 // Copyright 2020 Pixar
3 //
4 // Licensed under the Apache License, Version 2.0 (the "Apache License")
5 // with the following modification; you may not use this file except in
6 // compliance with the Apache License and the following modification to it:
7 // Section 6. Trademarks. is deleted and replaced with:
8 //
9 // 6. Trademarks. This License does not grant permission to use the trade
10 //    names, trademarks, service marks, or product names of the Licensor
11 //    and its affiliates, except as required to comply with Section 4(c) of
12 //    the License and to reproduce the content of the NOTICE file.
13 //
14 // You may obtain a copy of the Apache License at
15 //
16 //     http://www.apache.org/licenses/LICENSE-2.0
17 //
18 // Unless required by applicable law or agreed to in writing, software
19 // distributed under the Apache License with the above modification is
20 // distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
21 // KIND, either express or implied. See the Apache License for the specific
22 // language governing permissions and limitations under the Apache License.
23 //
24 #include "pxr/usd/usdLux/lightDefParser.h"
25 
26 #include "pxr/usd/usdLux/boundableLightBase.h"
27 #include "pxr/usd/usdLux/nonboundableLightBase.h"
28 
29 #include "pxr/usd/usdShade/connectableAPI.h"
30 #include "pxr/usd/usdShade/shaderDefUtils.h"
31 #include "pxr/usd/usdShade/tokens.h"
32 #include "pxr/usd/sdf/copyUtils.h"
33 #include "pxr/usd/sdr/shaderNode.h"
34 
35 #include "pxr/base/plug/plugin.h"
36 #include "pxr/base/plug/thisPlugin.h"
37 
38 PXR_NAMESPACE_OPEN_SCOPE
39 
40 TF_DEFINE_PRIVATE_TOKENS(
41     _tokens,
42 
43     /* discoveryTypes */
44     ((sourceType, "USD"))
45     ((discoveryType, "usd-schema-gen"))
46 
47     (MeshLight)
48     (MeshLightAPI)
49     (LightAPI)
50     (ShadowAPI)
51     (ShapingAPI)
52     (VolumeLight)
53     (VolumeLightAPI)
54 );
55 
56 /*static*/
57 const TfToken &
_GetSourceType()58 UsdLux_LightDefParserPlugin::_GetSourceType()
59 {
60     return _tokens->sourceType;
61 }
62 
63 /*static*/
64 const TfToken &
_GetDiscoveryType()65 UsdLux_LightDefParserPlugin::_GetDiscoveryType()
66 {
67     return _tokens->discoveryType;
68 }
69 
70 /*static*/
71 const UsdLux_LightDefParserPlugin::ShaderIdToAPITypeNameMap&
_GetShaderIdToAPITypeNameMap()72 UsdLux_LightDefParserPlugin::_GetShaderIdToAPITypeNameMap() {
73     static const UsdLux_LightDefParserPlugin::ShaderIdToAPITypeNameMap
74         shaderIdToAPITypeNameMap = {
75         {_tokens->MeshLight, _tokens->MeshLightAPI},
76         {_tokens->VolumeLight, _tokens->VolumeLightAPI}
77     };
78     return shaderIdToAPITypeNameMap;
79 }
80 
81 static
82 NdrTokenMap
_GetSdrMetadata(const UsdShadeConnectableAPI & connectable,const NdrTokenMap & discoveryResultMetadata)83 _GetSdrMetadata(const UsdShadeConnectableAPI &connectable,
84                 const NdrTokenMap &discoveryResultMetadata)
85 {
86     NdrTokenMap metadata = discoveryResultMetadata;
87 
88     metadata[SdrNodeMetadata->Help] = TfStringPrintf(
89         "Fallback shader node generated from the USD %s schema",
90         connectable.GetPrim().GetTypeName().GetText());
91 
92     metadata[SdrNodeMetadata->Primvars] =
93         UsdShadeShaderDefUtils::GetPrimvarNamesMetadataString(
94             metadata, connectable);
95 
96     return metadata;
97 }
98 
99 static SdfLayerRefPtr
_GetGeneratedSchema()100 _GetGeneratedSchema()
101 {
102     // Get the generateSchema file for this plugin and open it as a layer.
103     const std::string fname =
104         PLUG_THIS_PLUGIN->FindPluginResource("generatedSchema.usda", false);
105     SdfLayerRefPtr layer = SdfLayer::OpenAsAnonymous(fname);
106     return layer;
107 }
108 
109 static bool
_CopyPropertiesFromSchema(const SdfLayerRefPtr & schemaLayer,const TfToken & schemaName,const SdfPrimSpecHandle & destPrimSpec)110 _CopyPropertiesFromSchema(
111     const SdfLayerRefPtr &schemaLayer, const TfToken &schemaName,
112     const SdfPrimSpecHandle &destPrimSpec)
113 {
114     // The path of schema prim in the generated schema layer is its schema name.
115     const SdfPath schemaPath =
116         SdfPath::AbsoluteRootPath().AppendChild(schemaName);
117     SdfPrimSpecHandle schemaSpec = schemaLayer->GetPrimAtPath(schemaPath);
118     if (!schemaSpec) {
119         TF_CODING_ERROR("The generatedSchema for UsdLux does not have a prim "
120                         "spec for schema type '%s'.",
121                         schemaName.GetText());
122         return false;
123     }
124 
125     const SdfLayerHandle destLayer = destPrimSpec->GetLayer();
126     const SdfPath destPath = destPrimSpec->GetPath();
127     // Copy all the schema's properties to the destination.
128     for (const SdfPropertySpecHandle &propSpec : schemaSpec->GetProperties()) {
129         if (!SdfCopySpec(schemaLayer, propSpec->GetPath(),
130                 destLayer, destPath.AppendProperty(propSpec->GetNameToken()))) {
131             TF_CODING_ERROR("Could not copy property spec '%s' from "
132                             "generatedSchema for UsdLux schema '%s' to "
133                             "destination layer.",
134                             propSpec->GetPath().GetText(),
135                             schemaName.GetText());
136             return false;
137         }
138     }
139     return true;
140 }
141 
142 NdrNodeUniquePtr
Parse(const NdrNodeDiscoveryResult & discoveryResult)143 UsdLux_LightDefParserPlugin::Parse(
144     const NdrNodeDiscoveryResult &discoveryResult)
145 {
146     TRACE_FUNCTION();
147 
148     const UsdLux_LightDefParserPlugin::ShaderIdToAPITypeNameMap
149         &shaderIdToAPITypeNameMap = _GetShaderIdToAPITypeNameMap();
150 
151     // If discoveryResult identifier is a shaderId corresponding to one of the
152     // API schemas for which we are generating sdr representation, then go and
153     // fetch the name of the API schema which will then be used to extract
154     // properties from the generatedSchema
155     const TfToken &primTypeName =
156         (shaderIdToAPITypeNameMap.find(discoveryResult.identifier) ==
157          shaderIdToAPITypeNameMap.end()) ?
158             discoveryResult.identifier :
159             shaderIdToAPITypeNameMap.at(discoveryResult.identifier);
160 
161     // This parser wants to pull all the shader properties from the schema
162     // defined properties of the base UsdLux light type as well as the shader
163     // properties that can be included via applying the Shadow and Shaping APIs.
164     // However, it does NOT want to pull in any shader properties that could
165     // possibly come in from other plugins that may define API schemas that
166     // would auto apply to any of these lights (or to the LightAPI itself).
167     //
168     // Since the UsdSchemaRegistry doesn't keep track of what built-in API
169     // schemas a type's properties come from, we have to manually figure out the
170     // relevant properties from the UsdLux library's generatedSchema layer and
171     // compose them into a new prim that will represent the base light
172     // definition. This prim can then be opened on a stage in order to use the
173     // UsdShadeConnectableAPI to get all the inputs and outputs.
174 
175     // Find and open the generated schema layer.
176     SdfLayerRefPtr schemaLayer = _GetGeneratedSchema();
177     if (!schemaLayer) {
178         return NdrParserPlugin::GetInvalidNode(discoveryResult);
179     }
180 
181     // Since we're composing the prim ourselves create a new layer and prim
182     // spec where we'll add all the properties.
183     SdfLayerRefPtr layer = SdfLayer::CreateAnonymous(".usd");
184     SdfPrimSpecHandle primSpec = SdfPrimSpec::New(
185         layer, primTypeName, SdfSpecifierDef);
186 
187     // All of the UsdLux intrinsic lights will directly include LightAPI so it
188     // will have all the properties from LightAPI as well as any it defines
189     // itself. We also need to include the ShadowAPI and ShapingAPI properties
190     // as these can be optionally applied to any light. We copy the properties
191     // from each of the schema type prim specs over to the composed prim spec.
192     // Note, that the order we copy is important as the light type itself may
193     // have properties that override properties that come from the LightAPI.
194     const TfTokenVector schemas({
195         _tokens->LightAPI,
196         primTypeName,
197         _tokens->ShadowAPI,
198         _tokens->ShapingAPI});
199     for (const TfToken &schemaName : schemas) {
200         // It's important that we copy just the properties. Prim fields like
201         // the typeName, apiSchemas, and the property children can affect what
202         // properties are included when we open this prim on a USD stage.
203         if (!_CopyPropertiesFromSchema(schemaLayer, schemaName, primSpec)) {
204             return NdrParserPlugin::GetInvalidNode(discoveryResult);
205         }
206     }
207 
208     // Open a stage with the layer and get the new prim as a UsdConnectableAPI
209     // which we'll create the node from.
210     UsdStageRefPtr stage = UsdStage::Open(layer, nullptr);
211     if (!stage) {
212         return NdrParserPlugin::GetInvalidNode(discoveryResult);
213     }
214     UsdPrim prim = stage->GetPrimAtPath(primSpec->GetPath());
215     if (!prim) {
216         return NdrParserPlugin::GetInvalidNode(discoveryResult);
217     }
218     // Note that we don't check the "conformance" of this prim to the
219     // connectable API because the prim is untyped and will not conform. But
220     // conformance isn't necessary for using UsdShadeConnectableAPI in order
221     // to get input and output properties from a prim as is require in the
222     // functions called below.
223     UsdShadeConnectableAPI connectable(prim);
224 
225     return NdrNodeUniquePtr(new SdrShaderNode(
226         discoveryResult.identifier,
227         discoveryResult.version,
228         discoveryResult.name,
229         discoveryResult.family,
230         SdrNodeContext->Light,
231         discoveryResult.sourceType,
232         /*nodeUriAssetPath=*/ std::string(),
233         /*resolvedImplementationUri=*/ std::string(),
234         UsdShadeShaderDefUtils::GetShaderProperties(connectable),
235         _GetSdrMetadata(connectable, discoveryResult.metadata),
236         discoveryResult.sourceCode
237     ));
238 }
239 
240 const NdrTokenVec &
GetDiscoveryTypes() const241 UsdLux_LightDefParserPlugin::GetDiscoveryTypes() const
242 {
243     static const NdrTokenVec discoveryTypes{_GetDiscoveryType()};
244     return discoveryTypes;
245 }
246 
247 const TfToken &
GetSourceType() const248 UsdLux_LightDefParserPlugin::GetSourceType() const
249 {
250     return _GetSourceType();
251 }
252 
253 NDR_REGISTER_PARSER_PLUGIN(UsdLux_LightDefParserPlugin);
254 
255 PXR_NAMESPACE_CLOSE_SCOPE
256