2
// Copyright 2016 Pixar
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:
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.
14
// You may obtain a copy of the Apache License at
16
// http://www.apache.org/licenses/LICENSE-2.0
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.
25
#include "usdMaya/MayaNurbsSurfaceWriter.h"
27
#include "pxr/usd/usdGeom/nurbsPatch.h"
28
#include "pxr/usd/usdGeom/nurbsCurves.h"
29
#include "pxr/usd/usdGeom/pointBased.h"
30
#include "pxr/usd/usd/stage.h"
31
#include "pxr/usd/usdUtils/pipeline.h"
33
#include <maya/MFnDependencyNode.h>
34
#include <maya/MFnNurbsSurface.h>
35
#include <maya/MFnNurbsCurve.h>
36
#include <maya/MTrimBoundaryArray.h>
37
#include <maya/MPointArray.h>
39
MayaNurbsSurfaceWriter::MayaNurbsSurfaceWriter(
42
const JobExportArgs & iArgs) :
43
MayaTransformWriter(iDag, stage, iArgs)
48
UsdPrim MayaNurbsSurfaceWriter::write(const UsdTimeCode &usdTimeCode)
51
UsdGeomNurbsPatch primSchema =
52
UsdGeomNurbsPatch::Define(getUsdStage(), getUsdPath());
54
UsdPrim prim = primSchema.GetPrim();
58
writeNurbsSurfaceAttrs(usdTimeCode, primSchema);
63
bool MayaNurbsSurfaceWriter::writeNurbsSurfaceAttrs(
64
const UsdTimeCode &usdTimeCode,
65
UsdGeomNurbsPatch &primSchema)
67
MStatus status = MS::kSuccess;
69
// Write parent class attrs
70
writeTransformAttrs(usdTimeCode, primSchema);
72
// Return if usdTimeCode does not match if shape is animated
73
if (usdTimeCode.IsDefault() == isShapeAnimated() ) {
74
// skip shape as the usdTimeCode does not match if shape isAnimated value
78
MFnNurbsSurface nurbs( getDagPath(), &status );
80
MGlobal::displayError(
81
"MFnNurbsSurface() failed for MayaNurbsSurfaceWriter" );
85
// Gather GPrim DisplayColor/DisplayOpacity
86
// We use the same code used for gathering shader data on a mesh
87
// but we pass 0 for the numfaces argument since there is no per face
88
// shader assignment possible.
89
if (getArgs().exportDisplayColor) {
90
VtArray<GfVec3f> RGBData;
92
VtArray<float> AlphaData;
94
if (PxrUsdMayaUtil::GetLinearShaderColor(nurbs, 0,
96
&AlphaData, &AlphaInterp)) {
97
if (RGBData.size()>0) {
98
UsdGeomPrimvar dispColor = primSchema.GetDisplayColorPrimvar();
99
if (RGBInterp != dispColor.GetInterpolation())
100
dispColor.SetInterpolation(RGBInterp);
101
dispColor.Set(RGBData);
103
if (AlphaData.size() > 0 &&
104
GfIsClose(AlphaData[0], 1.0, 1e-9)==false) {
105
UsdGeomPrimvar dispOpacity = primSchema.GetDisplayOpacityPrimvar();
106
if (AlphaInterp != dispOpacity.GetInterpolation())
107
dispOpacity.SetInterpolation(AlphaInterp);
108
dispOpacity.Set(AlphaData);
113
unsigned int numKnotsInU = nurbs.numKnotsInU();
114
unsigned int numKnotsInV = nurbs.numKnotsInV();
115
if (numKnotsInU < 2 || numKnotsInV < 2) {
116
MGlobal::displayError(
117
"MFnNurbsSurface() has degenerate knot vectors. Skippping..." );
121
MDoubleArray knotsInU;
122
nurbs.getKnotsInU(knotsInU);
123
MDoubleArray knotsInV;
124
nurbs.getKnotsInV(knotsInV);
127
double startU, endU, startV, endV;
128
nurbs.getKnotDomain(startU, endU, startV, endV);
130
// Offset and scale to normalize knots from 0 to 1
136
if (getArgs().normalizeNurbs) {
137
if (endU>startU && endV>startV) {
140
uScale = 1.0 / (endU - startU);
141
vScale = 1.0 / (endV - startV);
142
startU = 0; startV = 0;
147
GfVec2d uRange, vRange;
148
uRange[0]=startU; uRange[1]=endU;
149
vRange[0]=startV; vRange[1]=endV;
151
// pad the start and end with a knot on each side, since thats what
152
// most apps, like Houdini and Renderman want these two extra knots
153
VtArray<double> sampKnotsInU(numKnotsInU+2);
154
VtArray<double> sampKnotsInV(numKnotsInV+2);
156
for (unsigned int i = 0; i < numKnotsInU; i++) {
157
sampKnotsInU[i+1]=(double)((knotsInU[i]-uOffset)*uScale);
160
for (unsigned int i = 0; i < numKnotsInV; i++) {
161
sampKnotsInV[i+1]=(double)((knotsInV[i]-vOffset)*vScale);
164
sampKnotsInU[0] = (2.0 * sampKnotsInU[1] - sampKnotsInU[2]);
165
sampKnotsInU[numKnotsInU+1] = (2.0 * sampKnotsInU[numKnotsInU] -
166
sampKnotsInU[numKnotsInU-1]);
167
sampKnotsInV[0] = (2.0 * sampKnotsInV[1] - sampKnotsInV[2]);
168
sampKnotsInV[numKnotsInV+1] = (2.0 * sampKnotsInV[numKnotsInV] -
169
sampKnotsInV[numKnotsInV-1]);
172
nurbs.getCVs(cvArray, MSpace::kObject);
173
unsigned int numCVs = cvArray.length();
174
int numCVsInU = nurbs.numCVsInU();
175
int numCVsInV = nurbs.numCVsInV();
177
VtArray<GfVec3f> sampPos(numCVs);
178
VtArray<double> sampPosWeights(numCVs);
179
bool setWeights = false;
181
// Create st vec2f vertex primvar
182
// NOTE: We currently support PxUsdExportJobArgsTokens->Uniform
183
// So no need to do a check in its value yet
184
VtArray<GfVec2f> stValues;
185
if (getArgs().exportNurbsExplicitUV) {
186
stValues.resize(numCVsInU*numCVsInV);
189
// Maya stores the data where v varies the fastest (v,u order)
190
// so we need to pack the data differently u,v order
191
// WE DIFFER FROM ALEMBIC WRITER, WE DON'T FLIP V
193
for (int v = 0; v < numCVsInV; v++) {
194
for (int u = 0; u < numCVsInU; u++) {
195
int index = u * numCVsInV + v;
197
// Extract CV location and weight
198
sampPos[cvIndex].Set(cvArray[index].x, cvArray[index].y, cvArray[index].z);
199
sampPosWeights[cvIndex] = cvArray[index].w;
200
if (GfIsClose(cvArray[index].w, 1.0, 1e-9)==false) {
204
// Compute uniform ST values if stValues can hold it
205
// No need to check for nurbsTexCoordParam yet since we only
206
// support uniform in the code
207
if (stValues.size() > static_cast<size_t>(cvIndex)) {
208
float sValue = static_cast<float>(u)/static_cast<float>(numCVsInU-1);
209
float tValue = static_cast<float>(v)/static_cast<float>(numCVsInV-1);
210
stValues[cvIndex] = GfVec2f(sValue, tValue);
217
// Set Gprim Attributes
218
// Compute the extent using the CVs.
219
VtArray<GfVec3f> extent(2);
220
UsdGeomPointBased::ComputeExtent(sampPos, &extent);
221
primSchema.CreateExtentAttr().Set(extent, usdTimeCode);
223
// Set NurbsPatch attributes
224
primSchema.GetUVertexCountAttr().Set(numCVsInU);
225
primSchema.GetVVertexCountAttr().Set(numCVsInV);
226
primSchema.GetUOrderAttr().Set(nurbs.degreeU() + 1);
227
primSchema.GetVOrderAttr().Set(nurbs.degreeV() + 1);
228
primSchema.GetUKnotsAttr().Set(sampKnotsInU);
229
primSchema.GetVKnotsAttr().Set(sampKnotsInV);
230
primSchema.GetURangeAttr().Set(uRange);
231
primSchema.GetVRangeAttr().Set(vRange);
232
primSchema.GetPointsAttr().Set(sampPos, usdTimeCode);
234
primSchema.GetPointWeightsAttr().Set(sampPosWeights);
237
// If stValues vector has vertex data, create and assign st
238
if (stValues.size() == static_cast<size_t>(numCVsInU * numCVsInV)) {
239
UsdGeomPrimvar uvSet =
240
primSchema.CreatePrimvar(UsdUtilsGetPrimaryUVSetName(),
241
SdfValueTypeNames->Float2Array,
242
UsdGeomTokens->vertex);
243
uvSet.Set( stValues );
247
switch (nurbs.formInU()) {
248
case MFnNurbsSurface::kClosed:
249
primSchema.GetUFormAttr().Set(UsdGeomTokens->closed);
251
case MFnNurbsSurface::kPeriodic:
252
primSchema.GetUFormAttr().Set(UsdGeomTokens->periodic);
255
primSchema.GetUFormAttr().Set(UsdGeomTokens->open);
257
switch (nurbs.formInV()) {
258
case MFnNurbsSurface::kClosed:
259
primSchema.GetVFormAttr().Set(UsdGeomTokens->closed);
261
case MFnNurbsSurface::kPeriodic:
262
primSchema.GetVFormAttr().Set(UsdGeomTokens->periodic);
265
primSchema.GetVFormAttr().Set(UsdGeomTokens->open);
268
// If not trimmed surface, you are done
269
// ONLY TRIM CURVE CODE BEYOND THIS POINT
270
if (!nurbs.isTrimmedSurface()) {
274
unsigned int numRegions = nurbs.numRegions();
276
// each boundary is a curvegroup, it can have multiple trim curve segments
278
// A Maya's trimmed NURBS surface has multiple regions.
279
// Inside a region, there are multiple boundaries.
280
// There are one CCW outer boundary and optional CW inner boundaries.
281
// Each boundary is a closed boundary and consists of multiple curves.
282
// NOTE: Maya regions are flattened but thanks for the curve ordering
283
// we can reconstruct them at read time back into Maya
284
// USD has the same semantic as RenderMan.
285
// RenderMan's doc says: "The curves of a loop connect
286
// in head-to-tail fashion and must be explicitly closed. "
288
// A Maya boundary is equivalent to an USD/RenderMan loop
289
VtArray<int> trimNumCurves;
290
VtArray<int> trimNumPos;
291
VtArray<int> trimOrder;
292
VtArray<double> trimKnot;
293
VtArray<GfVec2d> trimRange;
294
VtArray<GfVec3d> trimPoint;
297
for (unsigned int i = 0; i < numRegions; i++)
299
MTrimBoundaryArray result;
301
// if the 3rd argument is set to be true, return the 2D curve
302
nurbs.getTrimBoundaries(result, i, true);
303
unsigned int numBoundaries = result.length();
305
for (unsigned int j = 0; j < numBoundaries; j++)
308
WE DON'T NEED THIS BUT IT'S HERE FOR POSSIBLE FUTURE USE
309
switch(fn.boundaryType(i,j)) {
310
case MFnNurbsSurface::kInner: break;
311
case MFnNurbsSurface::kOuter: break;
312
case MFnNurbsSurface::kSegment: break;
313
case MFnNurbsSurface::kClosedSegment: break;
318
MObjectArray boundary = result[j];
319
unsigned int numTrimCurve = boundary.length();
320
trimNumCurves.push_back(numTrimCurve);
322
for (unsigned int k = 0; k < numTrimCurve; k++)
324
MObject curveObj = boundary[k];
325
if (curveObj.hasFn(MFn::kNurbsCurve))
327
MFnNurbsCurve mFnCurve(curveObj);
329
int numCVs = mFnCurve.numCVs();
330
trimNumPos.push_back(numCVs);
331
trimOrder.push_back(mFnCurve.degree()+1);
334
mFnCurve.getKnotDomain(start, end);
338
trimRange.push_back(range);
341
mFnCurve.getCVs(cvArray);
342
// WE DIFFER FROM ALEMBIC WRITER, WE DON'T FLIP V
343
for (int l = 0; l < numCVs; l++)
347
point[0]=(double)((cvArray[l].x-uOffset)*uScale);
348
point[1]=(double)((cvArray[l].y-vOffset)*vScale);
349
point[2]=cvArray[l].w;
350
trimPoint.push_back(point);
354
mFnCurve.getKnots(knot);
355
unsigned int numKnots = knot.length();
357
// push_back a dummy value, we will set it below
358
std::size_t totalNumKnots = trimKnot.size();
359
trimKnot.push_back(0.0);
360
for (unsigned int l = 0; l < numKnots; l++)
362
trimKnot.push_back(knot[l]);
365
// for a knot sequence with multiple end knots, duplicate
366
// the existing first and last knots once more.
367
// for a knot sequence with uniform end knots, create their
368
// the new knots offset at an interval equal to the existing
369
// first and last knot intervals
370
double k1 = trimKnot[totalNumKnots+1];
371
double k2 = trimKnot[totalNumKnots+2];
372
double klast_1 = trimKnot[trimKnot.size()-1];
373
double klast_2 = trimKnot[trimKnot.size()-2];
374
trimKnot[totalNumKnots] = 2.0 * k1 - k2;
375
trimKnot.push_back(2.0 * klast_1 - klast_2);
381
primSchema.GetTrimCurveCountsAttr().Set(trimNumCurves);
382
primSchema.GetTrimCurveOrdersAttr().Set(trimOrder);
383
primSchema.GetTrimCurveVertexCountsAttr().Set(trimNumPos);
384
primSchema.GetTrimCurveKnotsAttr().Set(trimKnot);
385
primSchema.GetTrimCurveRangesAttr().Set(trimRange);
386
primSchema.GetTrimCurvePointsAttr().Set(trimPoint);
388
// NO NON TRIM CODE HERE SINCE WE RETURN EARLIER IF NOT TRIMMED
393
MayaNurbsSurfaceWriter::exportsGprims() const