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