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