1 // Copyright Contributors to the OpenVDB Project
2 // SPDX-License-Identifier: MPL-2.0
3
4 /// @author FX R&D OpenVDB team
5
6 #include "OpenVDBPlugin.h"
7 #include <openvdb_maya/OpenVDBData.h>
8 #include <openvdb_maya/OpenVDBUtil.h>
9
10 #include <openvdb/tools/MeshToVolume.h>
11 #include <openvdb/tools/LevelSetUtil.h>
12
13 #include <maya/MFnTypedAttribute.h>
14 #include <maya/MFloatPointArray.h>
15 #include <maya/MPointArray.h>
16 #include <maya/MItMeshPolygon.h>
17 #include <maya/MFnMeshData.h>
18 #include <maya/MFnStringData.h>
19 #include <maya/MFnPluginData.h>
20 #include <maya/MGlobal.h>
21 #include <maya/MFnMesh.h>
22 #include <maya/MFnNumericAttribute.h>
23
24 #include <memory> // std::auto_ptr
25
26 namespace mvdb = openvdb_maya;
27
28
29 ////////////////////////////////////////
30
31
32 struct OpenVDBFromPolygonsNode : public MPxNode
33 {
OpenVDBFromPolygonsNodeOpenVDBFromPolygonsNode34 OpenVDBFromPolygonsNode() {}
~OpenVDBFromPolygonsNodeOpenVDBFromPolygonsNode35 virtual ~OpenVDBFromPolygonsNode() {}
36
37 virtual MStatus compute(const MPlug& plug, MDataBlock& data);
38
39 static void* creator();
40 static MStatus initialize();
41
42 static MTypeId id;
43 static MObject aMeshInput;
44 static MObject aVdbOutput;
45 static MObject aExportDistanceGrid;
46 static MObject aDistanceGridName;
47 static MObject aExportDensityGrid;
48 static MObject aDensityGridName;
49 static MObject aVoxelSize;
50 static MObject aExteriorBandWidth;
51 static MObject aInteriorBandWidth;
52 static MObject aFillInterior;
53 static MObject aUnsignedDistanceField;
54 static MObject aEstimatedGridResolution;
55 static MObject aNodeInfo;
56 };
57
58
59 MTypeId OpenVDBFromPolygonsNode::id(0x00108A54);
60 MObject OpenVDBFromPolygonsNode::aMeshInput;
61 MObject OpenVDBFromPolygonsNode::aVdbOutput;
62 MObject OpenVDBFromPolygonsNode::aExportDistanceGrid;
63 MObject OpenVDBFromPolygonsNode::aDistanceGridName;
64 MObject OpenVDBFromPolygonsNode::aExportDensityGrid;
65 MObject OpenVDBFromPolygonsNode::aDensityGridName;
66 MObject OpenVDBFromPolygonsNode::aVoxelSize;
67 MObject OpenVDBFromPolygonsNode::aExteriorBandWidth;
68 MObject OpenVDBFromPolygonsNode::aInteriorBandWidth;
69 MObject OpenVDBFromPolygonsNode::aFillInterior;
70 MObject OpenVDBFromPolygonsNode::aUnsignedDistanceField;
71 MObject OpenVDBFromPolygonsNode::aEstimatedGridResolution;
72 MObject OpenVDBFromPolygonsNode::aNodeInfo;
73
74
75 namespace {
76 mvdb::NodeRegistry registerNode("OpenVDBFromPolygons", OpenVDBFromPolygonsNode::id,
77 OpenVDBFromPolygonsNode::creator, OpenVDBFromPolygonsNode::initialize);
78 }
79
80
81 ////////////////////////////////////////
82
83
84 void*
creator()85 OpenVDBFromPolygonsNode::creator()
86 {
87 return new OpenVDBFromPolygonsNode();
88 }
89
90
91 MStatus
initialize()92 OpenVDBFromPolygonsNode::initialize()
93 {
94 MStatus stat;
95 MFnTypedAttribute tAttr;
96 MFnNumericAttribute nAttr;
97
98 // Setup the input mesh attribute
99
100 MFnMeshData meshCreator;
101 MObject emptyMesh = meshCreator.create(&stat);
102 if (stat != MS::kSuccess) return stat;
103
104
105 MFnStringData fnStringData;
106 MObject distName = fnStringData.create("surface");
107 MObject densName = fnStringData.create("density");
108 MObject emptyStr = fnStringData.create("");
109
110 aMeshInput = tAttr.create("MeshInput", "mesh", MFnData::kMesh, emptyMesh, &stat);
111 if (stat != MS::kSuccess) return stat;
112
113 stat = addAttribute(aMeshInput);
114 if (stat != MS::kSuccess) return stat;
115
116
117 // Conversion settings
118
119 aExportDistanceGrid = nAttr.create(
120 "ExportDistanceVDB", "exportdistance", MFnNumericData::kBoolean);
121 nAttr.setDefault(true);
122 nAttr.setConnectable(false);
123
124 stat = addAttribute(aExportDistanceGrid);
125 if (stat != MS::kSuccess) return stat;
126
127 aDistanceGridName = tAttr.create(
128 "DistanceGridName", "distancename", MFnData::kString, distName, &stat);
129 if (stat != MS::kSuccess) return stat;
130
131 tAttr.setConnectable(false);
132 stat = addAttribute(aDistanceGridName);
133 if (stat != MS::kSuccess) return stat;
134
135
136 aExportDensityGrid = nAttr.create(
137 "ExportDensityVDB", "exportdensity", MFnNumericData::kBoolean);
138 nAttr.setDefault(false);
139 nAttr.setConnectable(false);
140
141 stat = addAttribute(aExportDensityGrid);
142 if (stat != MS::kSuccess) return stat;
143
144 aDensityGridName = tAttr.create(
145 "DensityGridName", "densityname", MFnData::kString, densName, &stat);
146 if (stat != MS::kSuccess) return stat;
147
148 tAttr.setConnectable(false);
149 stat = addAttribute(aDensityGridName);
150 if (stat != MS::kSuccess) return stat;
151
152
153 aVoxelSize = nAttr.create("VoxelSize", "voxelsize", MFnNumericData::kFloat);
154 nAttr.setDefault(1.0);
155 nAttr.setMin(1e-5);
156 nAttr.setSoftMin(0.0);
157 nAttr.setSoftMax(10.0);
158
159 stat = addAttribute(aVoxelSize);
160 if (stat != MS::kSuccess) return stat;
161
162
163 aExteriorBandWidth = nAttr.create(
164 "ExteriorBandWidth", "exteriorbandwidth", MFnNumericData::kFloat);
165 nAttr.setDefault(3.0);
166 nAttr.setMin(1.0);
167 nAttr.setSoftMin(1.0);
168 nAttr.setSoftMax(10.0);
169
170 stat = addAttribute(aExteriorBandWidth);
171 if (stat != MS::kSuccess) return stat;
172
173 aInteriorBandWidth = nAttr.create(
174 "InteriorBandWidth", "interiorbandwidth", MFnNumericData::kFloat);
175 nAttr.setDefault(3.0);
176 nAttr.setMin(1.0);
177 nAttr.setSoftMin(1.0);
178 nAttr.setSoftMax(10.0);
179
180 stat = addAttribute(aInteriorBandWidth);
181 if (stat != MS::kSuccess) return stat;
182
183
184 aFillInterior = nAttr.create("FillInterior", "fillinterior", MFnNumericData::kBoolean);
185 nAttr.setDefault(false);
186 nAttr.setConnectable(false);
187
188 stat = addAttribute(aFillInterior);
189 if (stat != MS::kSuccess) return stat;
190
191
192 aUnsignedDistanceField = nAttr.create(
193 "UnsignedDistanceField", "udf", MFnNumericData::kBoolean);
194 nAttr.setDefault(false);
195 nAttr.setConnectable(false);
196
197 stat = addAttribute(aUnsignedDistanceField);
198 if (stat != MS::kSuccess) return stat;
199
200
201 // Setup the output attributes
202
203 aVdbOutput = tAttr.create("VdbOutput", "vdb", OpenVDBData::id, MObject::kNullObj, &stat);
204 if (stat != MS::kSuccess) return stat;
205
206 tAttr.setWritable(false);
207 tAttr.setStorable(false);
208 stat = addAttribute(aVdbOutput);
209 if (stat != MS::kSuccess) return stat;
210
211 // VDB Info
212 aEstimatedGridResolution = tAttr.create(
213 "EstimatedGridResolution", "res", MFnData::kString, emptyStr, &stat);
214 if (stat != MS::kSuccess) return stat;
215 tAttr.setConnectable(false);
216 tAttr.setWritable(false);
217 stat = addAttribute(aEstimatedGridResolution);
218 if (stat != MS::kSuccess) return stat;
219
220 aNodeInfo = tAttr.create("NodeInfo", "info", MFnData::kString, emptyStr, &stat);
221 if (stat != MS::kSuccess) return stat;
222 tAttr.setConnectable(false);
223 tAttr.setWritable(false);
224 stat = addAttribute(aNodeInfo);
225 if (stat != MS::kSuccess) return stat;
226
227 // Set the attribute dependencies
228
229 attributeAffects(aMeshInput, aVdbOutput);
230 attributeAffects(aExportDistanceGrid, aVdbOutput);
231 attributeAffects(aDistanceGridName, aVdbOutput);
232 attributeAffects(aExportDensityGrid, aVdbOutput);
233 attributeAffects(aDensityGridName, aVdbOutput);
234 attributeAffects(aVoxelSize, aVdbOutput);
235 attributeAffects(aExteriorBandWidth, aVdbOutput);
236 attributeAffects(aInteriorBandWidth, aVdbOutput);
237 attributeAffects(aFillInterior, aVdbOutput);
238 attributeAffects(aUnsignedDistanceField, aVdbOutput);
239
240 attributeAffects(aMeshInput, aEstimatedGridResolution);
241 attributeAffects(aVoxelSize, aEstimatedGridResolution);
242
243 attributeAffects(aMeshInput, aNodeInfo);
244 attributeAffects(aExportDistanceGrid, aNodeInfo);
245 attributeAffects(aDistanceGridName, aNodeInfo);
246 attributeAffects(aExportDensityGrid, aNodeInfo);
247 attributeAffects(aDensityGridName, aNodeInfo);
248 attributeAffects(aVoxelSize, aNodeInfo);
249 attributeAffects(aExteriorBandWidth, aNodeInfo);
250 attributeAffects(aInteriorBandWidth, aNodeInfo);
251 attributeAffects(aFillInterior, aNodeInfo);
252 attributeAffects(aUnsignedDistanceField, aNodeInfo);
253
254 return MS::kSuccess;
255 }
256
257
258 ////////////////////////////////////////
259
260
261 MStatus
compute(const MPlug & plug,MDataBlock & data)262 OpenVDBFromPolygonsNode::compute(const MPlug& plug, MDataBlock& data)
263 {
264 MStatus status;
265
266 if (plug == aEstimatedGridResolution) {
267
268 const float voxelSize = data.inputValue(aVoxelSize, &status).asFloat();
269 if (status != MS::kSuccess) return status;
270 if (!(voxelSize > 0.0)) return MS::kFailure;
271
272 MDataHandle meshHandle = data.inputValue(aMeshInput, &status);
273 if (status != MS::kSuccess) return status;
274
275 MObject tmpObj = meshHandle.asMesh();
276 if (tmpObj == MObject::kNullObj) return MS::kFailure;
277
278 MObject obj = meshHandle.asMeshTransformed();
279 if (obj == MObject::kNullObj) return MS::kFailure;
280
281 MFnMesh mesh(obj);
282
283 MFloatPointArray vertexArray;
284 status = mesh.getPoints(vertexArray, MSpace::kWorld);
285 if (status != MS::kSuccess) return status;
286
287 openvdb::Vec3s pmin(std::numeric_limits<float>::max()),
288 pmax(-std::numeric_limits<float>::max());
289
290 for(unsigned i = 0, I = vertexArray.length(); i < I; ++i) {
291 pmin[0] = std::min(pmin[0], vertexArray[i].x);
292 pmin[1] = std::min(pmin[1], vertexArray[i].y);
293 pmin[2] = std::min(pmin[2], vertexArray[i].z);
294 pmax[0] = std::max(pmax[0], vertexArray[i].x);
295 pmax[1] = std::max(pmax[1], vertexArray[i].y);
296 pmax[2] = std::max(pmax[2], vertexArray[i].z);
297 }
298
299 pmax = (pmax - pmin) / voxelSize;
300
301 int xres = int(std::ceil(pmax[0]));
302 int yres = int(std::ceil(pmax[1]));
303 int zres = int(std::ceil(pmax[2]));
304
305 std::stringstream txt;
306 txt << xres << " x " << yres << " x " << zres << " voxels";
307
308 MString str = txt.str().c_str();
309 MDataHandle strHandle = data.outputValue(aEstimatedGridResolution);
310 strHandle.set(str);
311
312 return data.setClean(plug);
313
314 } else if (plug == aVdbOutput || plug == aNodeInfo) {
315
316 MDataHandle meshHandle = data.inputValue(aMeshInput, &status);
317 if (status != MS::kSuccess) return status;
318
319 {
320 MObject obj = meshHandle.asMesh();
321 if (obj == MObject::kNullObj) return MS::kFailure;
322 }
323
324 MObject obj = meshHandle.asMeshTransformed();
325 if (obj == MObject::kNullObj) return MS::kFailure;
326
327 MFnMesh mesh(obj);
328
329 mvdb::Timer computeTimer;
330 std::stringstream infoStr;
331
332 const bool exportDistanceGrid = data.inputValue(aExportDistanceGrid, &status).asBool();
333 const bool exportDensityGrid = data.inputValue(aExportDensityGrid, &status).asBool();
334
335 MFnPluginData pluginData;
336 pluginData.create(OpenVDBData::id, &status);
337
338 if (status != MS::kSuccess) {
339 MGlobal::displayError("Failed to create a new OpenVDBData object.");
340 return MS::kFailure;
341 }
342
343 OpenVDBData* vdb = static_cast<OpenVDBData*>(pluginData.data(&status));
344 if (status != MS::kSuccess || !vdb) return MS::kFailure;
345
346 MDataHandle outHandle = data.outputValue(aVdbOutput);
347
348 float voxelSize = data.inputValue(aVoxelSize, &status).asFloat();
349 if (status != MS::kSuccess) return status;
350 if (!(voxelSize > 0.0)) return MS::kFailure;
351
352 openvdb::math::Transform::Ptr transform;
353 try {
354 transform = openvdb::math::Transform::createLinearTransform(voxelSize);
355 } catch (openvdb::ArithmeticError) {
356 MGlobal::displayError("Invalid voxel size.");
357 return MS::kFailure;
358 }
359
360 MFloatPointArray vertexArray;
361 status = mesh.getPoints(vertexArray, MSpace::kWorld);
362 if (status != MS::kSuccess) return status;
363
364
365 openvdb::Vec3d pos;
366 std::vector<openvdb::Vec3s> pointList(vertexArray.length());
367 for(unsigned i = 0, I = vertexArray.length(); i < I; ++i) {
368 pos[0] = double(vertexArray[i].x);
369 pos[1] = double(vertexArray[i].y);
370 pos[2] = double(vertexArray[i].z);
371
372 pos = transform->worldToIndex(pos);
373
374 pointList[i][0] = float(pos[0]);
375 pointList[i][1] = float(pos[1]);
376 pointList[i][2] = float(pos[2]);
377 }
378
379 std::vector<openvdb::Vec4I> primList;
380
381 MIntArray vertices;
382 for (MItMeshPolygon mIt(obj); !mIt.isDone(); mIt.next()) {
383
384 mIt.getVertices(vertices);
385
386 if (vertices.length() < 3) {
387
388 MGlobal::displayWarning(
389 "Skipped unsupported geometry, single point or line primitive.");
390
391 } else if (vertices.length() > 4) {
392
393 MPointArray points;
394 MIntArray triangleVerts;
395 mIt.getTriangles(points, triangleVerts);
396
397 for(unsigned idx = 0; idx < triangleVerts.length(); idx+=3) {
398
399 openvdb::Vec4I prim(
400 triangleVerts[idx],
401 triangleVerts[idx+1],
402 triangleVerts[idx+2],
403 openvdb::util::INVALID_IDX);
404
405 primList.push_back(prim);
406 }
407
408 } else {
409 mIt.getVertices(vertices);
410 openvdb::Vec4I prim(vertices[0], vertices[1], vertices[2],
411 (vertices.length() < 4) ? openvdb::util::INVALID_IDX : vertices[3]);
412
413 primList.push_back(prim);
414 }
415 }
416
417
418 infoStr << "Input Mesh\n";
419 infoStr << " nr of points: " << vertexArray.length() << "\n";
420 infoStr << " nr of primitives: " << primList.size() << "\n";
421
422
423
424 if (exportDistanceGrid || exportDensityGrid) {
425
426 float exteriorBandWidth = data.inputValue(aExteriorBandWidth, &status).asFloat();
427 float interiorBandWidth = exteriorBandWidth;
428
429 int conversionFlags = 0;
430
431 // convert to SDF
432 if (!data.inputValue(aUnsignedDistanceField, &status).asBool()) {
433
434 if (!data.inputValue(aFillInterior, &status).asBool()) {
435 interiorBandWidth = data.inputValue(aInteriorBandWidth, &status).asFloat();
436 } else {
437 interiorBandWidth = std::numeric_limits<float>::max();
438 }
439
440 } else {
441 conversionFlags = openvdb::tools::UNSIGNED_DISTANCE_FIELD;
442 }
443
444 openvdb::tools::QuadAndTriangleDataAdapter<openvdb::Vec3s, openvdb::Vec4I>
445 theMesh(pointList, primList);
446
447 openvdb::FloatGrid::Ptr grid = openvdb::tools::meshToVolume<openvdb::FloatGrid>(
448 theMesh, *transform, exteriorBandWidth, interiorBandWidth, conversionFlags);
449
450 // export distance grid
451 if (exportDistanceGrid) {
452 std::string name = data.inputValue(aDistanceGridName, &status).asString().asChar();
453 if (!name.empty()) grid->setName(name);
454 vdb->insert(grid);
455 }
456
457 // export density grid
458 if (exportDensityGrid) {
459
460 std::string name = data.inputValue(aDensityGridName, &status).asString().asChar();
461 openvdb::FloatGrid::Ptr densityGrid;
462
463 if (exportDistanceGrid) {
464 densityGrid = grid->deepCopy();
465 } else {
466 densityGrid = grid;
467 }
468
469 openvdb::tools::sdfToFogVolume(*densityGrid);
470
471 if (!name.empty()) densityGrid->setName(name);
472 vdb->insert(densityGrid);
473 }
474
475 }
476
477 std::string elapsedTime = computeTimer.elapsedTime();
478 mvdb::printGridInfo(infoStr, *vdb);
479 infoStr << "Compute Time: " << elapsedTime << "\n";
480 mvdb::updateNodeInfo(infoStr, data, aNodeInfo);
481
482 outHandle.set(vdb);
483 return data.setClean(plug);
484 }
485
486 return MS::kUnknownParameter;
487 }
488