Panda3D
maya_funcs.cxx
1 // Filename: maya_funcs.cxx
2 // Created by: drose (16Feb00)
3 //
4 ////////////////////////////////////////////////////////////////////
5 //
6 // PANDA 3D SOFTWARE
7 // Copyright (c) Carnegie Mellon University. All rights reserved.
8 //
9 // All use of this software is subject to the terms of the revised BSD
10 // license. You should have received a copy of this license along
11 // with this source code in a file named "LICENSE."
12 //
13 ////////////////////////////////////////////////////////////////////
14 
15 #include "maya_funcs.h"
16 
17 #include "pre_maya_include.h"
18 #include <maya/MObject.h>
19 #include <maya/MAngle.h>
20 #include <maya/MFnDependencyNode.h>
21 #include <maya/MStatus.h>
22 #include <maya/MFnStringData.h>
23 #include <maya/MFnNumericData.h>
24 #include <maya/MPlugArray.h>
25 #include <maya/MPlug.h>
26 #include <maya/MFnAttribute.h>
27 #include <maya/MFnTypedAttribute.h>
28 #include <maya/MFnNumericAttribute.h>
29 #include <maya/MFnEnumAttribute.h>
30 #include <maya/MFnCompoundAttribute.h>
31 #include <maya/MFnMatrixData.h>
32 #include <maya/MMatrix.h>
33 #include "post_maya_include.h"
34 
35 ////////////////////////////////////////////////////////////////////
36 // Function: get_maya_plug
37 // Description: Gets the named MPlug associated, if any.
38 ////////////////////////////////////////////////////////////////////
39 bool
40 get_maya_plug(MObject &node, const string &attribute_name, MPlug &plug) {
41  MStatus status;
42  MFnDependencyNode node_fn(node, &status);
43  if (!status) {
44  maya_cat.error()
45  << "Object is a " << node.apiTypeStr() << ", not a DependencyNode.\n";
46  return false;
47  }
48 
49  MObject attr = node_fn.attribute(attribute_name.c_str(), &status);
50  if (!status) {
51  return false;
52  }
53 
54  MFnAttribute attr_fn(attr, &status);
55  if (!status) {
56  maya_cat.error()
57  << "Attribute " << attribute_name << " on " << node_fn.name().asChar()
58  << " is a " << attr.apiTypeStr() << ", not an Attribute.\n";
59  return false;
60  }
61 
62  plug = MPlug(node, attr);
63  return true;
64 }
65 
66 ////////////////////////////////////////////////////////////////////
67 // Function: is_connected
68 // Description: Returns true if the named connection exists on the
69 // node and is connected to anything, false otherwise.
70 ////////////////////////////////////////////////////////////////////
71 bool
72 is_connected(MObject &node, const string &attribute_name) {
73  MPlug plug;
74  if (!get_maya_plug(node, attribute_name, plug)) {
75  return false;
76  }
77 
78  return plug.isConnected();
79 }
80 
81 ////////////////////////////////////////////////////////////////////
82 // Function: has_attribute
83 // Description: Returns true if the node has the indicated attribute,
84 // false otherwise.
85 ////////////////////////////////////////////////////////////////////
86 bool
87 has_attribute(MObject &node, const string &attribute_name) {
88  MStatus status;
89  MFnDependencyNode node_fn(node, &status);
90  if (!status) {
91  maya_cat.error()
92  << "Object is a " << node.apiTypeStr() << ", not a DependencyNode.\n";
93  return false;
94  }
95 
96  node_fn.attribute(attribute_name.c_str(), &status);
97  if (!status) {
98  // No such attribute.
99  return false;
100  }
101  return true;
102 }
103 
104 ////////////////////////////////////////////////////////////////////
105 // Function: remove_attribute
106 // Description: Removes the named attribute from the indicated Maya
107 // node. Returns true if successful, false otherwise.
108 ////////////////////////////////////////////////////////////////////
109 bool
110 remove_attribute(MObject &node, const string &attribute_name) {
111  MStatus status;
112  MFnDependencyNode node_fn(node, &status);
113  if (!status) {
114  maya_cat.error()
115  << "Object is a " << node.apiTypeStr() << ", not a DependencyNode.\n";
116  return false;
117  }
118 
119  MObject attr = node_fn.attribute(attribute_name.c_str(), &status);
120  if (!status) {
121  return false;
122  }
123 
124  {
125  // Just to prove the the attr is, in fact, an Attribute.
126  // According to the Maya docs, we shouldn't leave the MFnAttribute
127  // object around while we remove the attribute, though.
128  MFnAttribute attr_fn(attr, &status);
129  if (!status) {
130  maya_cat.error()
131  << "Attribute " << attribute_name << " on " << node_fn.name().asChar()
132  << " is a " << attr.apiTypeStr() << ", not an Attribute.\n";
133  return false;
134  }
135  }
136 
137  MFnDependencyNode::MAttrClass type = node_fn.attributeClass(attr, &status);
138  if (!status) {
139  maya_cat.error()
140  << "Couldn't get class of attribute " << attribute_name << " on "
141  << node_fn.name().asChar() << ".\n";
142  return false;
143  }
144 
145  status = node_fn.removeAttribute(attr, type);
146  if (!status) {
147  maya_cat.error()
148  << "Couldn't remove attribute " << attribute_name << " from "
149  << node_fn.name().asChar() << ".\n";
150  return false;
151  }
152 
153  return true;
154 }
155 
156 ////////////////////////////////////////////////////////////////////
157 // Function: get_bool_attribute
158 // Description: Extracts the named boolean attribute from the
159 // MObject.
160 ////////////////////////////////////////////////////////////////////
161 bool
162 get_bool_attribute(MObject &node, const string &attribute_name,
163  bool &value) {
164  if (!has_attribute(node, attribute_name)) {
165  // For bool attributes only, we assume if the attribute is absent
166  // it's the same thing as being false.
167  return false;
168  }
169 
170  if (!get_maya_attribute(node, attribute_name, value)) {
171  maya_cat.warning()
172  << "Attribute " << attribute_name
173  << " does not have a bool value.\n";
174  describe_maya_attribute(node, attribute_name);
175  return false;
176  }
177  return true;
178 }
179 
180 ////////////////////////////////////////////////////////////////////
181 // Function: get_bool_attribute
182 // Description: Extracts the named angle in degrees from the
183 // MObject.
184 ////////////////////////////////////////////////////////////////////
185 bool
186 get_angle_attribute(MObject &node, const string &attribute_name,
187  double &value) {
188  MAngle maya_value;
189  if (!get_maya_attribute(node, attribute_name, maya_value)) {
190  maya_cat.warning()
191  << "Attribute " << attribute_name
192  << " does not have an angle value.\n";
193  describe_maya_attribute(node, attribute_name);
194  return false;
195  }
196  value = maya_value.asDegrees();
197  return true;
198 }
199 
200 ////////////////////////////////////////////////////////////////////
201 // Function: get_vec2_attribute
202 // Description: Extracts the named two-component vector from the
203 // MObject.
204 ////////////////////////////////////////////////////////////////////
205 bool
206 get_vec2_attribute(MObject &node, const string &attribute_name,
207  LVecBase2 &value) {
208  MStatus status;
209 
210  MObject vec2_object;
211  if (!get_maya_attribute(node, attribute_name, vec2_object)) {
212  maya_cat.warning()
213  << "Attribute " << attribute_name
214  << " does not have a vec2 object value.\n";
215  describe_maya_attribute(node, attribute_name);
216  return false;
217  }
218 
219  MFnNumericData data(vec2_object, &status);
220  if (!status) {
221  maya_cat.warning()
222  << "Attribute " << attribute_name << " is of type "
223  << vec2_object.apiTypeStr() << ", not a NumericData.\n";
224  return false;
225  }
226 
227  status = data.getData(value[0], value[1]);
228  if (!status) {
229  maya_cat.warning()
230  << "Unable to extract 2 floats from " << attribute_name
231  << ", of type " << vec2_object.apiTypeStr() << "\n";
232  }
233 
234  return true;
235 }
236 
237 ////////////////////////////////////////////////////////////////////
238 // Function: get_vec3_attribute
239 // Description: Extracts the named three-component vector from the
240 // MObject.
241 ////////////////////////////////////////////////////////////////////
242 bool
243 get_vec3_attribute(MObject &node, const string &attribute_name,
244  LVecBase3 &value) {
245  MStatus status;
246 
247  MObject vec3_object;
248  if (!get_maya_attribute(node, attribute_name, vec3_object)) {
249  maya_cat.warning()
250  << "Attribute " << attribute_name
251  << " does not have a vec3 object value.\n";
252  describe_maya_attribute(node, attribute_name);
253  return false;
254  }
255 
256  MFnNumericData data(vec3_object, &status);
257  if (!status) {
258  maya_cat.warning()
259  << "Attribute " << attribute_name << " is of type "
260  << vec3_object.apiTypeStr() << ", not a NumericData.\n";
261  return false;
262  }
263 
264  status = data.getData(value[0], value[1], value[2]);
265  if (!status) {
266  maya_cat.warning()
267  << "Unable to extract 3 floats from " << attribute_name
268  << ", of type " << vec3_object.apiTypeStr() << "\n";
269  }
270 
271  return true;
272 }
273 
274 ////////////////////////////////////////////////////////////////////
275 // Function: get_vec2d_attribute
276 // Description: Extracts the named two-component vector from the
277 // MObject.
278 ////////////////////////////////////////////////////////////////////
279 bool
280 get_vec2d_attribute(MObject &node, const string &attribute_name,
281  LVecBase2d &value) {
282  MStatus status;
283 
284  MObject vec2d_object;
285  if (!get_maya_attribute(node, attribute_name, vec2d_object)) {
286  maya_cat.warning()
287  << "Attribute " << attribute_name
288  << " does not have a vec2d object value.\n";
289  describe_maya_attribute(node, attribute_name);
290  return false;
291  }
292 
293  MFnNumericData data(vec2d_object, &status);
294  if (!status) {
295  maya_cat.warning()
296  << "Attribute " << attribute_name << " is of type "
297  << vec2d_object.apiTypeStr() << ", not a NumericData.\n";
298  return false;
299  }
300 
301  status = data.getData(value[0], value[1]);
302  if (!status) {
303  maya_cat.warning()
304  << "Unable to extract 2 doubles from " << attribute_name
305  << ", of type " << vec2d_object.apiTypeStr() << "\n";
306  }
307 
308  return true;
309 }
310 
311 ////////////////////////////////////////////////////////////////////
312 // Function: get_vec3d_attribute
313 // Description: Extracts the named three-component vector from the
314 // MObject.
315 ////////////////////////////////////////////////////////////////////
316 bool
317 get_vec3d_attribute(MObject &node, const string &attribute_name,
318  LVecBase3d &value) {
319  MStatus status;
320 
321  MObject vec3d_object;
322  if (!get_maya_attribute(node, attribute_name, vec3d_object)) {
323  maya_cat.warning()
324  << "Attribute " << attribute_name
325  << " does not have a vec3d object value.\n";
326  describe_maya_attribute(node, attribute_name);
327  return false;
328  }
329 
330  MFnNumericData data(vec3d_object, &status);
331  if (!status) {
332  maya_cat.warning()
333  << "Attribute " << attribute_name << " is of type "
334  << vec3d_object.apiTypeStr() << ", not a NumericData.\n";
335  return false;
336  }
337 
338  status = data.getData(value[0], value[1], value[2]);
339  if (!status) {
340  maya_cat.warning()
341  << "Unable to extract 3 doubles from " << attribute_name
342  << ", of type " << vec3d_object.apiTypeStr() << "\n";
343  }
344 
345  return true;
346 }
347 
348 ////////////////////////////////////////////////////////////////////
349 // Function: get_mat4d_attribute
350 // Description: Extracts the named 4x4 matrix from the MObject.
351 ////////////////////////////////////////////////////////////////////
352 bool
353 get_mat4d_attribute(MObject &node, const string &attribute_name,
354  LMatrix4d &value) {
355  MStatus status;
356  MObject matrix;
357  if (!get_maya_attribute(node, attribute_name, matrix)) {
358  return false;
359  }
360 
361  MFnMatrixData matrix_data(matrix, &status);
362  if (!status) {
363  maya_cat.warning()
364  << "Attribute " << attribute_name << " is of type "
365  << node.apiTypeStr() << ", not a Matrix.\n";
366  return false;
367  }
368 
369  const MMatrix &mat = matrix_data.matrix();
370  for (int i = 0; i < 4; i++) {
371  for (int j = 0; j < 4; j++) {
372  value(i, j) = mat(i, j);
373  }
374  }
375  return true;
376 }
377 
378 ////////////////////////////////////////////////////////////////////
379 // Function: get_tag_attribute_names
380 // Description: artists should be able to set arbitrary tags.
381 // Query all the attributes on this object and return
382 // the lists of attribute names that has "tag" prefix
383 ////////////////////////////////////////////////////////////////////
384 void
385 get_tag_attribute_names(MObject &node, pvector<string> &tag_names) {
386  MStatus status;
387  MFnDependencyNode node_fn(node, &status);
388  if (!status) {
389  maya_cat.warning()
390  << "Object is a " << node.apiTypeStr() << ", not a DependencyNode.\n";
391  return;
392  }
393 
394  string name = node_fn.name().asChar();
395  unsigned i;
396 
397  for (i = 0; i < node_fn.attributeCount(); i++) {
398  MObject attr = node_fn.attribute(i, &status);
399  if (status) {
400  MFnAttribute attrib(attr, &status);
401  if (status) {
402  string attribute_name = attrib.name().asChar();
403  if (attribute_name.find("tag", 0) != string::npos) {
404  maya_cat.info() << ":" << name << ":" << " is tagged with <"
405  << attribute_name << ">" << endl;
406  tag_names.push_back(attribute_name);
407  }
408  }
409  }
410  }
411 }
412 ////////////////////////////////////////////////////////////////////
413 // Function: get_enum_attribute
414 // Description: Extracts the enum attribute from the MObject as a
415 // string value.
416 ////////////////////////////////////////////////////////////////////
417 bool
418 get_enum_attribute(MObject &node, const string &attribute_name,
419  string &value) {
420  MStatus status;
421 
422  MPlug plug;
423  if (!get_maya_plug(node, attribute_name.c_str(), plug)) {
424  return false;
425  }
426 
427  MObject attrib = plug.attribute();
428  MFnEnumAttribute enum_attrib(attrib, &status);
429  if (!status) {
430  maya_cat.warning()
431  << "Not an enum attribute: " << attribute_name << "\n";
432  return false;
433  }
434 
435  short index;
436  status = plug.getValue(index);
437  if (!status) {
438  maya_cat.warning()
439  << "Could not get numeric value of " << attribute_name << "\n";
440  status.perror("MPlug::getValue(short)");
441  return false;
442  }
443 
444  MString name = enum_attrib.fieldName(index, &status);
445  if (!status) {
446  maya_cat.warning()
447  << "Invalid value for " << attribute_name << ": " << index << "\n";
448  status.perror("MFnEnumAttribute::fieldName()");
449  return false;
450  }
451 
452  value = name.asChar();
453  return true;
454 }
455 
456 ////////////////////////////////////////////////////////////////////
457 // Function: get_string_attribute
458 // Description: Extracts the named string attribute from the
459 // MObject.
460 ////////////////////////////////////////////////////////////////////
461 bool
462 get_string_attribute(MObject &node, const string &attribute_name,
463  string &value) {
464  MStatus status;
465 
466  MObject string_object;
467  if (!get_maya_attribute(node, attribute_name, string_object)) {
468  maya_cat.warning()
469  << "Attribute " << attribute_name
470  << " does not have an string object value.\n";
471  describe_maya_attribute(node, attribute_name);
472  return false;
473  }
474 
475  MFnStringData data(string_object, &status);
476  if (!status) {
477  maya_cat.warning()
478  << "Attribute " << attribute_name << " is of type "
479  << string_object.apiTypeStr() << ", not a StringData.\n";
480  return false;
481  }
482 
483  value = data.string().asChar();
484  return true;
485 }
486 
487 ////////////////////////////////////////////////////////////////////
488 // Function: set_string_attribute
489 // Description: Sets the named string attribute on the
490 // MObject.
491 ////////////////////////////////////////////////////////////////////
492 bool
493 set_string_attribute(MObject &node, const string &attribute_name,
494  const string &value) {
495  MStatus status;
496 
497  // First, we get the string_object, then we set its string.
498  MObject string_object;
499  if (!get_maya_attribute(node, attribute_name, string_object)) {
500  maya_cat.warning()
501  << "Attribute " << attribute_name
502  << " does not have a string object value.\n";
503  describe_maya_attribute(node, attribute_name);
504  return false;
505  }
506 
507  MFnStringData data(string_object, &status);
508  if (!status) {
509  maya_cat.warning()
510  << "Attribute " << attribute_name << " is of type "
511  << string_object.apiTypeStr() << ", not a StringData.\n";
512  return false;
513  }
514 
515  MString mstring_value(value.data(), value.length());
516  status = data.set(mstring_value);
517  if (!status) {
518  status.perror(attribute_name.c_str());
519  return false;
520  }
521 
522  // And it appears we now need to set the string object back.
523  if (!set_maya_attribute(node, attribute_name, string_object)) {
524  maya_cat.warning()
525  << "Attribute " << attribute_name
526  << " suddenly does not have a string object value.\n";
527  return false;
528  }
529 
530  return true;
531 }
532 
533 ////////////////////////////////////////////////////////////////////
534 // Function: describe_compound_attribute
535 // Description: Extracts the children of this attribute from the
536 // MObject. test for now
537 ////////////////////////////////////////////////////////////////////
538 bool
539 describe_compound_attribute(MObject &node) {
540  MStatus status;
541 
542  MFnCompoundAttribute comp_attr(node, &status);
543 
544  maya_cat.info() << "comp_attr has:" << comp_attr.numChildren() << " children" << endl;
545  for (size_t i = 0; i < comp_attr.numChildren(); i++) {
546  MObject child = comp_attr.child(i, &status);
547  if (child.apiType() == MFn::kAttribute3Float){
549  /*
550  if (get_vec3_attribute(child, "color", color)) {
551  maya_cat.info() << "color: " << color << endl;
552  }
553  */
554  }
555  else if (child.apiType() == MFn::kNumericAttribute) {
556  MFnNumericAttribute numeric(child, &status);
557  if (status) {
558  switch(numeric.unitType()) {
559  case MFnNumericData::kFloat :
560  PN_stdfloat alpha;
561  status = numeric.getDefault(alpha);
562  maya_cat.info() << "found a float :" << alpha << endl;
563  break;
564  case MFnNumericData::kBoolean :
565  bool v;
566  status = numeric.getDefault(v);
567  maya_cat.info() << "found a bool :" << v << endl;
568  default:
569  maya_cat.info() << numeric.unitType() << endl;
570  }
571  }
572  }
573  else if (child.apiType() == MFn::kEnumAttribute) {
574  MFnEnumAttribute enu(child, &status);
575  if (status) {
576  MString blah;
577  status = enu.getDefault(blah);
578  maya_cat.info() << "found a string :" << blah.asChar() << endl;
579  MPlug plug = MPlug(node, child);
580  maya_cat.info() << "plug name" << plug.name().asChar() << endl;
581  }
582  }
583  }
584  return true;
585 }
586 
587 ////////////////////////////////////////////////////////////////////
588 // Function: describe_maya_attribute
589 // Description: Writes some warning output about the indicated Maya
590 // attribute.
591 ////////////////////////////////////////////////////////////////////
592 void
593 describe_maya_attribute(MObject &node, const string &attribute_name) {
594  MStatus status;
595  MFnDependencyNode node_fn(node, &status);
596  if (!status) {
597  maya_cat.warning()
598  << "Object is a " << node.apiTypeStr() << ", not a DependencyNode.\n";
599  return;
600  }
601 
602  MObject attr = node_fn.attribute(attribute_name.c_str(), &status);
603  if (!status) {
604  maya_cat.warning()
605  << "Object " << node_fn.name().asChar() << " does not support attribute "
606  << attribute_name << "\n";
607  return;
608  }
609 
610  maya_cat.warning()
611  << "Attribute " << attribute_name << " on object "
612  << node_fn.name().asChar() << " has type " << attr.apiTypeStr() << "\n";
613 }
614 
615 string
616 string_mfndata_type(MFnData::Type type) {
617  switch (type) {
618  case MFnData::kInvalid:
619  return "kInvalid";
620 
621  case MFnData::kNumeric:
622  return "kNumeric";
623 
624  case MFnData::kPlugin:
625  return "kPlugin";
626 
627  case MFnData::kPluginGeometry:
628  return "kPluginGeometry";
629 
630  case MFnData::kString:
631  return "kString";
632 
633  case MFnData::kMatrix:
634  return "kMatrix";
635 
636  case MFnData::kStringArray:
637  return "kStringArray";
638 
639  case MFnData::kDoubleArray:
640  return "kDoubleArray";
641 
642  case MFnData::kIntArray:
643  return "kIntArray";
644 
645  case MFnData::kPointArray:
646  return "kPointArray";
647 
648  case MFnData::kVectorArray:
649  return "kVectorArray";
650 
651  case MFnData::kComponentList:
652  return "kComponentList";
653 
654  case MFnData::kMesh:
655  return "kMesh";
656 
657  case MFnData::kLattice:
658  return "kLattice";
659 
660  case MFnData::kNurbsCurve:
661  return "kNurbsCurve";
662 
663  case MFnData::kNurbsSurface:
664  return "kNurbsSurface";
665 
666  case MFnData::kSphere:
667  return "kSphere";
668 
669  case MFnData::kDynArrayAttrs:
670  return "kDynArrayAttrs";
671 
672  case MFnData::kDynSweptGeometry:
673  return "kDynSweptGeometry";
674 
675  case MFnData::kSubdSurface:
676  return "kSubdSurface";
677 
678  case MFnData::kLast:
679  default:
680  break;
681  }
682 
683  return "**invalid**";
684 }
685 
686 ////////////////////////////////////////////////////////////////////
687 // Function: list_maya_attributes
688 // Description: Writes some info output showing all the attributes on
689 // the given dependency node. Primarily useful during
690 // development, to figure out where the heck Maya hides
691 // some of the connected properties.
692 ////////////////////////////////////////////////////////////////////
693 void
694 list_maya_attributes(MObject &node) {
695  MStatus status;
696  MFnDependencyNode node_fn(node, &status);
697  if (!status) {
698  maya_cat.warning()
699  << "Object is a " << node.apiTypeStr() << ", not a DependencyNode.\n";
700  return;
701  }
702 
703  string name = node_fn.name().asChar();
704  unsigned i;
705 
706  MPlugArray connections;
707  status = node_fn.getConnections(connections);
708  if (!status) {
709  status.perror("MFnDependencyNode::getConnections");
710 
711  } else {
712  maya_cat.info()
713  << name << " has " << connections.length() << " connections.\n";
714  for (i = 0; i < connections.length(); i++) {
715  MPlug plug = connections[i];
716 
717  maya_cat.info(false)
718  << " " << i << ". " << plug.name().asChar() << ", "
719  << plug.attribute().apiTypeStr() << ", "
720  << plug.node().apiTypeStr();
721  if (plug.attribute().apiType() == MFn::kCompoundAttribute) {
722  //maya_cat.info() << plug.info();
723  //describe_compound_attribute(plug.attribute());
724  }
725  if (plug.isConnected()) {
726  maya_cat.info(false)
727  << " (*)";
728  }
729  maya_cat.info(false)
730  << "\n";
731  }
732  }
733 
734  maya_cat.info()
735  << name << " has " << node_fn.attributeCount() << " attributes.\n";
736  for (i = 0; i < node_fn.attributeCount(); i++) {
737  MObject attr = node_fn.attribute(i, &status);
738  if (status) {
739  MFnTypedAttribute typed_attrib(attr, &status);
740  if (status) {
741  // It's a typed attrib.
742  maya_cat.info(false)
743  << " " << i << ". " << typed_attrib.name().asChar()
744  << " [" << attr.apiTypeStr() << ", "
745  << string_mfndata_type(typed_attrib.attrType()) << "]\n";
746  } else {
747  MFnAttribute attrib(attr, &status);
748  if (status) {
749  // It's a generic attrib.
750  maya_cat.info(false)
751  << " " << i << ". " << attrib.name().asChar()
752  << " [" << attr.apiTypeStr() << "]\n";
753  } else {
754  // Don't know what it is.
755  maya_cat.info(false)
756  << " " << i << ". [" << attr.apiTypeStr() << "]\n";
757  }
758  }
759  }
760  }
761 }
762 
This is the base class for all three-component vectors and points.
Definition: lvecBase3.h:105
This is a 4-by-4 transform matrix.
Definition: lmatrix.h:4716
This is the base class for all two-component vectors and points.
Definition: lvecBase2.h:1257
This is the base class for all two-component vectors and points.
Definition: lvecBase2.h:105
This is the base class for all three-component vectors and points.
Definition: lvecBase3.h:1471