1 // Copyright Contributors to the OpenVDB Project
2 // SPDX-License-Identifier: MPL-2.0
3 
4 /// @file OpenVDBVisualizeNode.cc
5 ///
6 /// @author FX R&D OpenVDB team
7 
8 #include "OpenVDBPlugin.h"
9 #include <openvdb_maya/OpenVDBData.h>
10 #include <openvdb_maya/OpenVDBUtil.h>
11 
12 #include <openvdb/io/Stream.h>
13 
14 #include <maya/MFnTypedAttribute.h>
15 #include <maya/MFnStringData.h>
16 #include <maya/MFnPluginData.h>
17 #include <maya/MFnNumericAttribute.h>
18 #include <maya/MPxLocatorNode.h>
19 #include <maya/MString.h>
20 #include <maya/MPlug.h>
21 #include <maya/MDataBlock.h>
22 #include <maya/MDataHandle.h>
23 #include <maya/MColor.h>
24 #include <maya/M3dView.h>
25 
26 
27 namespace mvdb = openvdb_maya;
28 
29 
30 ////////////////////////////////////////
31 
32 
33 struct OpenVDBVisualizeNode : public MPxLocatorNode
34 {
35     OpenVDBVisualizeNode();
36     virtual ~OpenVDBVisualizeNode();
37 
38     virtual MStatus compute(const MPlug& plug, MDataBlock& data);
39 
40     virtual void draw(M3dView & view, const MDagPath & path,
41         M3dView::DisplayStyle style, M3dView::DisplayStatus status);
42 
43     virtual bool isBounded() const;
44     virtual MBoundingBox boundingBox() const;
45 
46     static void * creator();
47     static  MStatus initialize();
48 
49     static MObject aVdbInput;
50     static MObject aVdbAllGridNames;
51     static MObject aVdbSelectedGridNames;
52 
53     static MObject aVisualizeBBox;
54     static MObject aVisualizeInternalNodes;
55     static MObject aVisualizeLeafNodes;
56     static MObject aVisualizeActiveTiles;
57     static MObject aVisualizeActiveVoxels;
58     static MObject aVisualizeSurface;
59     static MObject aIsovalue;
60 
61     static MObject aCachedBBox;
62     static MObject aCachedInternalNodes;
63     static MObject aCachedLeafNodes;
64     static MObject aCachedActiveTiles;
65     static MObject aCachedActiveVoxels;
66     static MObject aCachedSurface;
67 
68 
69     static MTypeId id;
70 
71 private:
72     std::vector<mvdb::BufferObject> mBBoxBuffers;
73     std::vector<mvdb::BufferObject> mNodeBuffers;
74     std::vector<mvdb::BufferObject> mLeafBuffers;
75     std::vector<mvdb::BufferObject> mTileBuffers;
76     std::vector<mvdb::BufferObject> mSurfaceBuffers;
77     std::vector<mvdb::BufferObject> mPointBuffers;
78 
79     mvdb::ShaderProgram mSurfaceShader, mPointShader;
80     MBoundingBox mBBox;
81 };
82 
83 
84 ////////////////////////////////////////
85 
86 
87 MObject OpenVDBVisualizeNode::aVdbInput;
88 MObject OpenVDBVisualizeNode::aVdbAllGridNames;
89 MObject OpenVDBVisualizeNode::aVdbSelectedGridNames;
90 
91 MObject OpenVDBVisualizeNode::aVisualizeBBox;
92 MObject OpenVDBVisualizeNode::aVisualizeInternalNodes;
93 MObject OpenVDBVisualizeNode::aVisualizeLeafNodes;
94 MObject OpenVDBVisualizeNode::aVisualizeActiveTiles;
95 MObject OpenVDBVisualizeNode::aVisualizeActiveVoxels;
96 MObject OpenVDBVisualizeNode::aVisualizeSurface;
97 MObject OpenVDBVisualizeNode::aIsovalue;
98 
99 MObject OpenVDBVisualizeNode::aCachedBBox;
100 MObject OpenVDBVisualizeNode::aCachedInternalNodes;
101 MObject OpenVDBVisualizeNode::aCachedLeafNodes;
102 MObject OpenVDBVisualizeNode::aCachedActiveTiles;
103 MObject OpenVDBVisualizeNode::aCachedActiveVoxels;
104 MObject OpenVDBVisualizeNode::aCachedSurface;
105 
106 MTypeId OpenVDBVisualizeNode::id(0x00108A53);
107 
108 
109 namespace {
110     mvdb::NodeRegistry registerNode("OpenVDBVisualize", OpenVDBVisualizeNode::id,
111         OpenVDBVisualizeNode::creator, OpenVDBVisualizeNode::initialize, MPxNode::kLocatorNode);
112 }
113 
114 
115 ////////////////////////////////////////
116 
117 
OpenVDBVisualizeNode()118 OpenVDBVisualizeNode::OpenVDBVisualizeNode()
119 {
120     mSurfaceShader.setVertShader(
121         "#version 120\n"
122         "varying vec3 normal;\n"
123         "void main() {\n"
124             "normal = normalize(gl_NormalMatrix * gl_Normal);\n"
125             "gl_Position =  ftransform();\n"
126             "gl_ClipVertex = gl_ModelViewMatrix * gl_Vertex;\n"
127         "}\n");
128 
129     mSurfaceShader.setFragShader(
130         "#version 120\n"
131         "varying vec3 normal;\n"
132         "const vec4 skyColor = vec4(0.9, 0.9, 1.0, 1.0);\n"
133         "const vec4 groundColor = vec4(0.3, 0.3, 0.2, 1.0);\n"
134         "void main() {\n"
135             "vec3 normalized_normal = normalize(normal);\n"
136             "float w = 0.5 * (1.0 + dot(normalized_normal, vec3(0.0, 1.0, 0.0)));\n"
137             "vec4 diffuseColor = w * skyColor + (1.0 - w) * groundColor;\n"
138             "gl_FragColor = diffuseColor;\n"
139         "}\n");
140 
141     mSurfaceShader.build();
142 
143     mPointShader.setVertShader(
144         "#version 120\n"
145         "varying vec3 normal;\n"
146         "void main() {\n"
147             "gl_FrontColor = gl_Color;\n"
148             "normal = normalize(gl_NormalMatrix * gl_Normal);\n"
149             "gl_Position =  ftransform();\n"
150             "gl_ClipVertex = gl_ModelViewMatrix * gl_Vertex;\n"
151         "}\n");
152 
153     mPointShader.setFragShader(
154         "#version 120\n"
155         "varying vec3 normal;\n"
156         "void main() {\n"
157             "vec3 normalized_normal = normalize(normal);\n"
158             "float w = 0.5 * (1.0 + dot(normalized_normal, vec3(0.0, 1.0, 0.0)));\n"
159             "vec4 diffuseColor = w * gl_Color + (1.0 - w) * (gl_Color * 0.3);\n"
160             "gl_FragColor = diffuseColor;\n"
161         "}\n");
162 
163     mPointShader.build();
164 }
165 
~OpenVDBVisualizeNode()166 OpenVDBVisualizeNode::~OpenVDBVisualizeNode()
167 {
168 }
169 
170 
creator()171 void* OpenVDBVisualizeNode::creator()
172 {
173     return new OpenVDBVisualizeNode();
174 }
175 
176 
initialize()177 MStatus OpenVDBVisualizeNode::initialize()
178 {
179     MStatus stat;
180     MFnNumericAttribute nAttr;
181     MFnTypedAttribute tAttr;
182 
183     // Setup input / output attributes
184 
185     aVdbInput = tAttr.create("VDBInput", "input", OpenVDBData::id, MObject::kNullObj, &stat);
186     if (stat != MS::kSuccess) return stat;
187     tAttr.setReadable(false);
188 
189     stat = addAttribute(aVdbInput);
190     if (stat != MS::kSuccess) return stat;
191 
192     // Setup UI attributes
193 
194     aVisualizeBBox = nAttr.create("ActiveValueBoundingBox", "bbox", MFnNumericData::kBoolean);
195     nAttr.setDefault(true);
196     nAttr.setConnectable(false);
197 
198     stat = addAttribute(aVisualizeBBox);
199     if (stat != MS::kSuccess) return stat;
200 
201     aVisualizeInternalNodes = nAttr.create("InternalNodes", "inodes", MFnNumericData::kBoolean);
202     nAttr.setDefault(false);
203     nAttr.setConnectable(false);
204 
205     stat = addAttribute(aVisualizeInternalNodes);
206     if (stat != MS::kSuccess) return stat;
207 
208     aVisualizeLeafNodes = nAttr.create("LeafNodes", "lnodes", MFnNumericData::kBoolean);
209     nAttr.setDefault(true);
210     nAttr.setConnectable(false);
211 
212     stat = addAttribute(aVisualizeLeafNodes);
213     if (stat != MS::kSuccess) return stat;
214 
215     aVisualizeActiveTiles = nAttr.create("ActiveTiles", "tiles", MFnNumericData::kBoolean);
216     nAttr.setDefault(true);
217     nAttr.setConnectable(false);
218 
219     stat = addAttribute(aVisualizeActiveTiles);
220     if (stat != MS::kSuccess) return stat;
221 
222     aVisualizeActiveVoxels = nAttr.create("ActiveVoxels", "voxels", MFnNumericData::kBoolean);
223     nAttr.setDefault(false);
224     nAttr.setConnectable(false);
225 
226     stat = addAttribute(aVisualizeActiveVoxels);
227     if (stat != MS::kSuccess) return stat;
228 
229     aVisualizeSurface = nAttr.create("Surface", "surface", MFnNumericData::kBoolean);
230     nAttr.setDefault(false);
231     nAttr.setConnectable(false);
232 
233     stat = addAttribute(aVisualizeSurface);
234     if (stat != MS::kSuccess) return stat;
235 
236     aIsovalue = nAttr.create("Isovalue", "iso", MFnNumericData::kFloat);
237     nAttr.setDefault(0.0);
238     nAttr.setSoftMin(-1.0);
239     nAttr.setSoftMax( 1.0);
240     nAttr.setConnectable(false);
241 
242     stat = addAttribute(aIsovalue);
243     if (stat != MS::kSuccess) return stat;
244 
245     // Setup internal attributes
246 
247     aCachedBBox = nAttr.create("cachedbbox", "cbb", MFnNumericData::kBoolean);
248     nAttr.setDefault(false);
249     nAttr.setWritable(false);
250     nAttr.setReadable(false);
251     nAttr.setHidden(true);
252 
253     stat = addAttribute(aCachedBBox);
254     if (stat != MS::kSuccess) return stat;
255 
256     aCachedInternalNodes = nAttr.create("cachedinternalnodes", "cin", MFnNumericData::kBoolean);
257     nAttr.setDefault(false);
258     nAttr.setWritable(false);
259     nAttr.setReadable(false);
260     nAttr.setHidden(true);
261 
262     stat = addAttribute(aCachedInternalNodes);
263     if (stat != MS::kSuccess) return stat;
264 
265     aCachedLeafNodes = nAttr.create("cachedleafnodes", "cln", MFnNumericData::kBoolean);
266     nAttr.setDefault(false);
267     nAttr.setWritable(false);
268     nAttr.setReadable(false);
269     nAttr.setHidden(true);
270 
271     stat = addAttribute(aCachedLeafNodes);
272     if (stat != MS::kSuccess) return stat;
273 
274     aCachedActiveTiles = nAttr.create("cachedactivetiles", "cat", MFnNumericData::kBoolean);
275     nAttr.setDefault(false);
276     nAttr.setWritable(false);
277     nAttr.setReadable(false);
278     nAttr.setHidden(true);
279 
280     stat = addAttribute(aCachedActiveTiles);
281     if (stat != MS::kSuccess) return stat;
282 
283     aCachedActiveVoxels = nAttr.create("cachedactivevoxels", "cav", MFnNumericData::kBoolean);
284     nAttr.setDefault(false);
285     nAttr.setWritable(false);
286     nAttr.setReadable(false);
287     nAttr.setHidden(true);
288 
289     stat = addAttribute(aCachedActiveVoxels);
290     if (stat != MS::kSuccess) return stat;
291 
292     aCachedSurface = nAttr.create("cachedsurface", "cs", MFnNumericData::kBoolean);
293     nAttr.setDefault(false);
294     nAttr.setWritable(false);
295     nAttr.setReadable(false);
296     nAttr.setHidden(true);
297 
298     stat = addAttribute(aCachedSurface);
299     if (stat != MS::kSuccess) return stat;
300 
301     MFnStringData fnStringData;
302     MObject defaultStringData = fnStringData.create("");
303 
304     aVdbAllGridNames = tAttr.create("VdbAllGridNames", "allgrids", MFnData::kString, defaultStringData, &stat);
305     if (stat != MS::kSuccess) return stat;
306     tAttr.setConnectable(false);
307     tAttr.setWritable(false);
308     tAttr.setReadable(false);
309     tAttr.setHidden(true);
310 
311     stat = addAttribute(aVdbAllGridNames);
312     if (stat != MS::kSuccess) return stat;
313 
314     aVdbSelectedGridNames =
315         tAttr.create("VdbSelectedGridNames", "selectedgrids", MFnData::kString, defaultStringData, &stat);
316     if (stat != MS::kSuccess) return stat;
317     tAttr.setConnectable(false);
318     tAttr.setWritable(false);
319     tAttr.setReadable(false);
320     tAttr.setHidden(true);
321 
322     stat = addAttribute(aVdbSelectedGridNames);
323     if (stat != MS::kSuccess) return stat;
324 
325     // Setup dependencies
326 
327     stat = attributeAffects(aVdbInput, aVdbAllGridNames);
328     if (stat != MS::kSuccess) return stat;
329 
330     stat = attributeAffects(aVdbInput, aCachedBBox);
331     if (stat != MS::kSuccess) return stat;
332 
333     stat = attributeAffects(aVdbInput, aCachedInternalNodes);
334     if (stat != MS::kSuccess) return stat;
335 
336     stat = attributeAffects(aVdbInput, aCachedLeafNodes);
337     if (stat != MS::kSuccess) return stat;
338 
339     stat = attributeAffects(aVdbInput, aCachedActiveTiles);
340     if (stat != MS::kSuccess) return stat;
341 
342     stat = attributeAffects(aVdbInput, aCachedActiveVoxels);
343     if (stat != MS::kSuccess) return stat;
344 
345     stat = attributeAffects(aVdbInput, aCachedSurface);
346     if (stat != MS::kSuccess) return stat;
347 
348     stat = attributeAffects(aVdbSelectedGridNames, aCachedBBox);
349     if (stat != MS::kSuccess) return stat;
350 
351     stat = attributeAffects(aVdbSelectedGridNames, aCachedInternalNodes);
352     if (stat != MS::kSuccess) return stat;
353 
354     stat = attributeAffects(aVdbSelectedGridNames, aCachedLeafNodes);
355     if (stat != MS::kSuccess) return stat;
356 
357     stat = attributeAffects(aVdbSelectedGridNames, aCachedActiveTiles);
358     if (stat != MS::kSuccess) return stat;
359 
360     stat = attributeAffects(aVdbSelectedGridNames, aCachedActiveVoxels);
361     if (stat != MS::kSuccess) return stat;
362 
363     stat = attributeAffects(aVdbSelectedGridNames, aCachedSurface);
364     if (stat != MS::kSuccess) return stat;
365 
366     stat = attributeAffects(aIsovalue, aCachedSurface);
367     if (stat != MS::kSuccess) return stat;
368 
369     return MS::kSuccess;
370 }
371 
372 
373 ////////////////////////////////////////
374 
375 
compute(const MPlug & plug,MDataBlock & data)376 MStatus OpenVDBVisualizeNode::compute(const MPlug& plug, MDataBlock& data)
377 {
378     MStatus status;
379 
380     const OpenVDBData* inputVdb = mvdb::getInputVDB(aVdbInput, data);
381     if (!inputVdb) return MS::kFailure;
382 
383     if (plug == aVdbAllGridNames) {
384         MString names = mvdb::getGridNames(*inputVdb).c_str();
385         MDataHandle outHandle = data.outputValue(aVdbAllGridNames);
386         outHandle.set(names);
387         return data.setClean(plug);
388     }
389 
390     // Get selected grids
391 
392     MDataHandle selectionHandle = data.inputValue(aVdbSelectedGridNames, &status);
393 
394     if (status != MS::kSuccess) return status;
395     std::string names = selectionHandle.asString().asChar();
396 
397     std::vector<openvdb::GridBase::ConstPtr> grids;
398     mvdb::getGrids(grids, *inputVdb, names);
399 
400     if (grids.empty()) {
401         mBBoxBuffers.clear();
402         mNodeBuffers.clear();
403         mLeafBuffers.clear();
404         mTileBuffers.clear();
405         mSurfaceBuffers.clear();
406         mPointBuffers.clear();
407         return MS::kUnknownParameter;
408     }
409 
410 
411     if (plug == aCachedInternalNodes) {
412         mNodeBuffers.clear();
413         mNodeBuffers.resize(grids.size());
414 
415         for (size_t n = 0, N = grids.size(); n < N; ++n) {
416             mvdb::InternalNodesGeo drawNodes(mNodeBuffers[n]);
417             mvdb::processTypedGrid(grids[n], drawNodes);
418         }
419 
420         MDataHandle outHandle = data.outputValue(aCachedInternalNodes);
421         outHandle.set(true);
422 
423     } else if (plug == aCachedLeafNodes) {
424         mLeafBuffers.clear();
425         mLeafBuffers.resize(grids.size());
426 
427         for (size_t n = 0, N = grids.size(); n < N; ++n) {
428             mvdb::LeafNodesGeo drawLeafs(mLeafBuffers[n]);
429             mvdb::processTypedGrid(grids[n], drawLeafs);
430         }
431 
432         MDataHandle outHandle = data.outputValue(aCachedLeafNodes);
433         outHandle.set(true);
434 
435     } else if (plug == aCachedBBox) {
436         MPoint pMin, pMax;
437 
438         mBBoxBuffers.clear();
439         mBBoxBuffers.resize(grids.size());
440 
441         for (size_t n = 0, N = grids.size(); n < N; ++n) {
442             mvdb::BoundingBoxGeo drawBBox(mBBoxBuffers[n]);
443             drawBBox(grids[n]);
444 
445             for (int i = 0; i < 3; ++i) {
446                 pMin[i] = drawBBox.min()[i];
447                 pMax[i] = drawBBox.max()[i];
448             }
449         }
450         mBBox = MBoundingBox(pMin, pMax);
451 
452         MDataHandle outHandle = data.outputValue(aCachedBBox);
453         outHandle.set(true);
454 
455     } else if (plug == aCachedActiveTiles) {
456         mTileBuffers.clear();
457         mTileBuffers.resize(grids.size());
458 
459         for (size_t n = 0, N = grids.size(); n < N; ++n) {
460             mvdb::ActiveTileGeo drawTiles(mTileBuffers[n]);
461             mvdb::processTypedGrid(grids[n], drawTiles);
462         }
463 
464         MDataHandle outHandle = data.outputValue(aCachedActiveTiles);
465         outHandle.set(true);
466 
467     } else if(plug == aCachedActiveVoxels) {
468         mPointBuffers.clear();
469         mPointBuffers.resize(grids.size());
470 
471         for (size_t n = 0, N = grids.size(); n < N; ++n) {
472             mvdb::ActiveVoxelGeo drawVoxels(mPointBuffers[n]);
473             mvdb::processTypedScalarGrid(grids[n], drawVoxels);
474         }
475 
476         MDataHandle outHandle = data.outputValue(aCachedActiveVoxels);
477         outHandle.set(true);
478 
479     } else if (plug == aCachedSurface) {
480         float iso = data.inputValue(aIsovalue, &status).asFloat();
481         if (status != MS::kSuccess) return status;
482 
483         mSurfaceBuffers.clear();
484         mSurfaceBuffers.resize(grids.size());
485 
486         for (size_t n = 0, N = grids.size(); n < N; ++n) {
487             mvdb::SurfaceGeo drawSurface(mSurfaceBuffers[n], iso);
488             mvdb::processTypedScalarGrid(grids[n], drawSurface);
489         }
490 
491         MDataHandle outHandle = data.outputValue(aCachedSurface);
492         outHandle.set(true);
493 
494     } else {
495         return MS::kUnknownParameter;
496     }
497 
498     return data.setClean(plug);
499 }
500 
501 
502 void
draw(M3dView & view,const MDagPath &,M3dView::DisplayStyle,M3dView::DisplayStatus status)503 OpenVDBVisualizeNode::draw(M3dView & view, const MDagPath& /*path*/,
504         M3dView::DisplayStyle /*style*/, M3dView::DisplayStatus status)
505 {
506     MObject thisNode = thisMObject();
507 
508     const bool isSelected = (status == M3dView::kActive) || (status == M3dView::kLead);
509 
510     const bool internalNodes    = MPlug(thisNode, aVisualizeInternalNodes).asBool();
511     const bool leafNodes        = MPlug(thisNode, aVisualizeLeafNodes).asBool();
512     const bool bbox             = MPlug(thisNode, aVisualizeBBox).asBool();
513     const bool tiles            = MPlug(thisNode, aVisualizeActiveTiles).asBool();
514     const bool voxels           = MPlug(thisNode, aVisualizeActiveVoxels).asBool();
515     const bool surface          = MPlug(thisNode, aVisualizeSurface).asBool();
516 
517     view.beginGL();
518 
519     if (surface && MPlug(thisNode, aCachedSurface).asBool()) {
520         if (!view.selectMode()) mSurfaceShader.startShading();
521         for (size_t n = 0, N = mSurfaceBuffers.size(); n < N; ++n) {
522             mSurfaceBuffers[n].render();
523         }
524         mSurfaceShader.stopShading();
525     }
526 
527     if (tiles && MPlug(thisNode, aCachedActiveTiles).asBool()) {
528         for (size_t n = 0, N = mTileBuffers.size(); n < N; ++n) {
529             mTileBuffers[n].render();
530         }
531     }
532 
533     if (leafNodes && MPlug(thisNode, aCachedLeafNodes).asBool()) {
534         for (size_t n = 0, N = mLeafBuffers.size(); n < N; ++n) {
535             mLeafBuffers[n].render();
536         }
537     }
538 
539     if (voxels && MPlug(thisNode, aCachedActiveVoxels).asBool()) {
540         if (!view.selectMode()) mPointShader.startShading();
541         for (size_t n = 0, N = mPointBuffers.size(); n < N; ++n) {
542             mPointBuffers[n].render();
543         }
544         mPointShader.stopShading();
545     }
546 
547     if (!view.selectMode()) {
548 
549         if (internalNodes && MPlug(thisNode, aCachedInternalNodes).asBool()) {
550 
551             for (size_t n = 0, N = mNodeBuffers.size(); n < N; ++n) {
552                 mNodeBuffers[n].render();
553             }
554         }
555 
556         if ((isSelected || bbox) && MPlug(thisNode, aCachedBBox).asBool()) {
557 
558             if (isSelected) glColor3f(0.9f, 0.9f, 0.3f);
559             else glColor3f(0.045f, 0.045f, 0.045f);
560 
561             for (size_t n = 0, N = mBBoxBuffers.size(); n < N; ++n) {
562                 mBBoxBuffers[n].render();
563             }
564         }
565     }
566 
567     view.endGL();
568 }
569 
570 
571 bool
isBounded() const572 OpenVDBVisualizeNode::isBounded() const
573 {
574     return true;
575 }
576 
577 
578 MBoundingBox
boundingBox() const579 OpenVDBVisualizeNode::boundingBox() const
580 {
581     bool cachedBBox = false;
582     MObject thisNode = thisMObject();
583     MPlug(thisNode, aCachedBBox).getValue(cachedBBox);
584     if (cachedBBox) return mBBox;
585 
586     return MBoundingBox(MPoint(-1.0, -1.0, -1.0), MPoint(1.0, 1.0, 1.0));
587 }
588