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