It is possible to expose C++ functions to maxscript interfaces as a property, rather an as a function. A LayerProperties
interface for example has properties such as on
, lock
, current
etc. that can be read and written to. These properties remain as C++ level functions, but are accessed as properties in MAXScript by using the standard dot-notation for members.
L = layermanager.getlayer 1
<MixinInterface:LayerProperties>
showinterface L
Interface: LayerProperties
Properties:
.on : boolean : Read|Write
.lock : boolean : Read|Write
.current : boolean : Read|Write
.wireColor : color by value : Read|Write
.isGIExcluded : boolean : Read|Write
...
--For example
L.on
true
L.on = false
false
It is possible to define selected methods in an FPInterface
as 'property accessor' methods so that dispatch-based clients of the interface may present these methods as properties instead of functions. A 'property' is typically defined by a pair of accessor methods, one for getting and one for setting, but you can also define read-only methods that have a single getter method. As an example, here's a rework of the previous Cylinder interface in which the radius and direction are made properties.
classCylInterface : public FPMixinInterface
{
...
virtualvoid RemoveCaps()=0;
virtualvoid AddBend(float offset, float angle, float radius)=0;
...
virtualfloat GetRadius()=0;
virtualvoid SetRadius(float radius)=0;
virtualPoint3 GetDirection()=0;
virtualvoid SetDirection(Point3 dir)=0;
...
BEGIN_FUNCTION_MAP
...
VFN_0(cyl_removeCaps, RemoveCaps);
VFN_3(cyl_addBend, AddBend, TYPE_FLOAT, TYPE_ANGLE, TYPE_FLOAT);
...
PROP_FNS(cyl_getRadius, GetRadius, cyl_setRadius, SetRadius, TYPE_FLOAT);
PROP_FNS(cyl_getDir, GetDirection, cy_setDir, SetDirection, TYPE_POINT3_BV);
...
END_FUNCTION_MAP
FPInterfaceDesc* GetDesc();
};
Each property has a getter and setter virtual method. Their signatures must conform to the convention shown, namely, the getter takes no parameters and the getter one. The getter returns the same type as the setter and the setter type is void. There are also variants that take an explicit time as described below.
Properties have single entries in the function map, using one of the property-related entry macros. In this case, PROP_FNS()
takes a getter ID, getter function, setter ID, setter function and finally the property type code. There is also a RO_PROP_FN()
macro for read-only properties that takes a getter ID, getter FN and prop type. Finally, the are variants of PROP_FNS
and RO_PROP_FN
that indicate the accessors take an explicit TimeValue
argument, presumably associated with animatable properties and indicating that the property access should be at the given time. These macros are PROP_TFNS
and RO_PROP_TFN
and takes the same arguments as their corresponding base macros. If the above example properties were time-sensitive, the definitions would be as follows:
classCylInterface : public FPMixinInterface
{
...
virtualvoid RemoveCaps()=0;
virtualvoid AddBend(float offset, float angle, float radius)=0;
...
virtualfloat GetRadius(TimeValue t)=0;
virtualvoid SetRadius(float radius, TimeValue t)=0;
virtualPoint3 GetDirection(TimeValue t)=0;
virtualvoid SetDirection(Point3 dir, TimeValue t)=0;
...
BEGIN_FUNCTION_MAP
...
VFN_0(cyl_removeCaps, RemoveCaps);
VFN_3(cyl_addBend, AddBend, TYPE_FLOAT, TYPE_ANGLE, TYPE_FLOAT);
...
PROP_TFNS(cyl_getRadius, GetRadius, cyl_setRadius, SetRadius, TYPE_FLOAT);
PROP_TFNS(cyl_getDir, GetDirection, cy_setDir, SetDirection, TYPE_POINT3_BV);
...
END_FUNCTION_MAP
FPInterfaceDesc* GetDesc();
};
Recall that there are time-sensitive variants for the FN_0
, FN_1
, etc., macros, namely TFN_0
, TFN_1
, etc. Functions specified by such macros in the function map have an implicit TimeValue
last parameter. Note that in all the time-sensitive variants, the last TimeValue
parameter is not defined as an explicit parameter in the FPInterface
descriptor entry for that function. Such time-sensitive properties and functions are handled specially in MAXScript. It supplies the current MAXScript time, as defined by the current 'at time' context for this implicit parameter, making these functions and properties behave consistently with the rest of MAXScript.
Properties are defined in a special section in the FPInterface
descriptor constructor following the function definitions, headed by the special tag 'properties'. They are also entered in the function map using new property-specific FUNCTION_MAP
macros. Here's an example descriptor fragment:
staticFPInterfaceDesc cylfpi (
CYL_INTERFACE, _T("cylMixin"), 0, &cylinderDesc, FP_MIXIN,
...
cyl_removeCaps, _T("removeCaps"), 0, TYPE_VOID, 0, 0,
cyl_addBend, _T("addBend"), 0, TYPE_VOID, 0, 3,
_T("offset"), 0, TYPE_FLOAT,
_T("angle"), 0, TYPE_ANGLE,
_T("radius"), 0, TYPE_FLOAT,
properties,
cyl_getRadius, cyl_setRadius, _T("radius"), IDS_RADIUS,
TYPE_FLOAT, f_range, 0.0, 10000.0,
cyl_getDirection, cyl_setDirection, _T("direction"), IDS_DIR,
TYPE_POINT3_BV,
...
end
);
The 'properties' section follows the function definitions. Each propery has a single entry defining the function IDs for the getter and setter functions, a fixed internal property name, a descriptor string resource ID and the property type. If the property is read-only and there is no setter function, specify FP_NO_FUNCTION
for the setter ID. Each property definition can optionally be followed by parameter validation options, as described in the Parameter Validation section above, and these apply to the parameter given to the setter function during property assignment. In this case, the radius is range checked.
If you need to get at it, the property metadata is accessible in the 'props' data member in an FPInterfaceDesc
. It is a Tab
<> of pointers to FPPropDef
class instances, which contain individual property metadata. See the FPPropDef
class definition in iFnPub
.h for details.
MAXScript now exposes properties defined in this way as direct dot-notation properties on the interface. So, where before the Get/SetRadius in the example cylinder mixin would have been accessed as functions:
r = $cyl01.cylMixin.getRadius()
$cyl01.cylMixin.setRadius 23
you now can use:
r = $cyl01.cylMixin.radius
$cyl01.cylMixin.radius = 23
This is true for all the types of FP interfaces that turn up in MAXScript, static, mixin and core. As a futher optimization, MAXScript now effectively promotes all interface methods and properties to the level of the interface, so if individual methods and properties have unqiue names within all the interfaces of an object or
class, you can eliminate the interface name. The above examples could now be written:
r = $cyl01.getRadius()
$cyl01.setRadius 23
and:
r = $cyl01.radius
$cyl01. radius = 23
If there is a naming conflict, you can always include the interface name level to resolve this.