What's New: 3ds Max 2020 SDK
This topic describes new items and changes that affect the 3ds Max 2020 C++ SDK.
- SDK Break
- Microsoft Visual Studio 2017
- UI
- Plugin Package Requirements
- Modifier Plugin Class Changes to Support Pb1 to Pb2 Migration
- Modeling
- Rendering
- Miscellaneous
- Deprecated APIs
SDK Break
The 3ds Max 2020 SDK is not binary backward compatible with the 3ds Max 2019 SDK. This means that plugins will need to be re-compiled in order to work in 3ds Max 2020. See the SDK Requirements topic for more information on the platform requirements for building plugins in 3ds Max 2020.
Microsoft Visual Studio 2017
3ds Max 2020 is compiled with Microsoft Visual Studio 2017 version 15.8.3, Platform Toolset v141, and Windows Platform SDK version 10.0.17134.0. plugins need to be built using the same Platform Toolset version. Any version of Microsoft Visual Studio can be used as long as the Platform Toolset used is v141.
Note that installers for earlier versions of Microsoft Visual Studio 2017 are available here.
Switch from Fast to Precise Math Computation
3ds Max 2019 and many previous versions have been compiled using the "fast math" optimization of the Visual Studio C++ compiler (see /fp:fast
VC++ compile option documentation).
In Visual Studio 2017 version 15.3 and 15.5, the code optimization for the /fp:fast
option has been further modified to provide even faster code than in Visual Studio 2015.
Because the speed improvement comes with some loss in precision, after careful consideration of the impact on both 3ds Max and 3rd party plugins, we decided to switch from using the "fast math" optimization to building with the "precise math" compiler option (see '/fp:precise' VC++ compile option documentation).
Based on our testing, switching to "precise math" does not impact 3ds Max's performance in any significant way, and it has the following benefits:
- Protects from future optimizations in the Visual Studio compilers, which might lead to visible differences in 3ds Max features that can inflate small discrepancies in floating point numbers into large and very visible artifacts on meshes, for example;
- Enables a compiler warning when a floating point value is truncated/rounded and a loss of accuracy happens. Under "fast math" this was previously ignored by the compiler. We have fixed over 900 such warnings.
Note that 3rd party code can continue to use "fast math" if needed; switching to "precise math" is not mandatory. However, be aware that there is a significant change in the way
Sin()
andCos()
are optimized when called side by side with the same argument when using "fast math" that reduces the accuracy of the result. The new behavior is documented here.
UI
Qt Support for Parameter Blocks Based UI
Class ClassDesc2_Extension2017SP2
has been deprecated, and the method below has been moved to class ClassDesc2
:
// Deprecated. Please overwrite the method with the same signature from class ClassDesc2
virtual MaxSDK::QMaxParamBlockWidget* ClassDesc2_Extension2017SP2::CreateQtWidget(
ReferenceMaker& owner,
IParamBlock2& paramBlock,
const MapID paramMapID,
MSTR& rollupTitle,
int& rollupFlags,
int& rollupCategory,
Class_ID& tabID) = 0;
Qt Support in the 3ds Max Plugin Wizard
The 3ds Max Plugin Wizard has been updated to support generating code for Qt-based user interfaces for Utility Object, Geometric Object and Modifier plugin types. See the Plugin Wizard readme and the Using Qt with 3ds Max Plug-ins topic for more information about using Qt for plug-in user interfaces.
MAXScript RolloutControl
If a third party rollout control supports a controller object (an instance of class Control), it will need to derive from the new class AnimatableRolloutControl
rather than class RolloutControl
. Any implementation code that currently accesses directly the "controller" data member, will need to call Control* RolloutControl::get_controller()
instead.
Plugin Package Requirements
In versions of 3ds Max prior to 2020, the UpgradeCode
specified by a plugin package's PackageContents.xml manifest file was ignored since plugin packages were identified uniquely by their ProductCode
.
Since the technology used by the Autodesk Developer Network to generate installers for plugin packages relies on the UpgradeCode
to remain unchanged when a package with an updated plugin is released, while the ProductCode
needs to change every time the plugin (or application) version is updated, 3ds Max 2020's plugin package discovery and loading mechanism has been updated.
The manifest of plugin packages need to adhere to the following requirements:
- The
UpgradeCode
is required and should not change between updates of a particular plugin. For example, if the first release of a plugin is plugin_v1.msi, and the second version is plugin_v2.msi, the UpgradeCode for the two versions should stay the same. This allows the Windows installer technology to find plugin_v1 on the system and update it to plugin_v2, without asking the user to uninstall plugin_v1 first. - The
ProductCode
is required by the Autodesk AppStore (though not required by 3ds Max) and should change for each new version of the plugin, i.e. whenever theAppVersion
is incremented For example, if the first release of a plugin is plugin_v1.msi, and the second version is plugin_v2.msi, theProductCode
for the two versions should be different.
3ds Max 2020 and newer will not load plugin packages that have no UpgradeCode
. When two or more plugin packages with the same UpgradeCode
are found, the package with the higher AppVersion
is loaded.
Note that the plugin package loading mechanism has been exposed to the SDK; see class
PluginPackageManager
in maxsdk\include\pluginpackagemanager\pluginpackagemanager.h
Note that the MAXScript exposure of the package loading mechanism has been changed:
The "ExchangeStorePackageManager" interface has been renamed to "PluginPackageManager", although the previous name will be aliased to the new one
The method pathConfig.GetExchangeStorePlugInInstallPath()
has been removed. Please use instead GetDir #publicExchangeStoreInstallPath()
Modifier Plugin Class Changes to Support Pb1 to Pb2 Migration
In order to ease the migration of parameter block 1 (PB1) based object space modifiers (OSM) to parameter block 2 (PB2), the SimpleMod
and SimpleMod2
classes both derive from a new class called SimpleModBase
. Furtheremore, the PB1 and PB2 members of class SimpleMod
, and class SimpleMod2
respectively were made protected. Class SimpleModBase
is basically handling everything class SimpleMod
used to, except the PB1, which is handled by class SimpleMod
.
Impact on plugins that derive from class SimpleMod:
The only impact on 3rd party plugins that derive from class SimpleMod
is a small non-mandatory change in their Clone method: remove the call that clones their PB1 since SimpleMod::SimpleModClone()
now takes care of it.
In the following sample, the code that needs to be removed from current implementations of ReferenceTarget::Clone()
overrides is commented out.
RefTargetHandle MyModifier::Clone(RemapDir& remap)
{
MyModifier* newmod = new MyModifier();
// Cloning of the PB1 is taken care of by SimpleMod::SimpleModClone()
// newmod->ReplaceReference(SIMPMOD_PBLOCKREF,remap.CloneRef(pblock));
newmod->SimpleModClone(this, remap);
BaseClone(this, newmod, remap);
return (newmod);
}
There's no impact on plugins that derive from class SimpleMod2
.
Impact on code that accesses SimpleMod
's parameter block:
Code that needs access to the PB1 held by a SimpleMod
instance needs to use the IParamArray* BaseObject::GetParamBlock()
accessor method. One can then use the IParamBlock* IParamArray::GetParamBlock()
method to get to the PB1.
Impact on code that accesses SimpleMod2
's parameter block:
Code that needs access to the PB2 held by an Animatable
instance needs to use one of the following two methods:
IParamBlock2* Animatabe::GetParamBlock(int i)
IParamBlock2* Animatable::GetParamBlockByID(BlockID id)
Modeling
Triangulation Algorithms
The algorithms used for triangulating mesh faces have been exposed through new functions: BestConvexDiagonals()
, FindDiagonals()
, and GetTriangles()
.
Furthermore, the old DiagSort()
function has been renamed to SortPolygonDiagonals()
and moved next to the new functions.
API to Calculate Intersection of 3D Lines
The following function was added to the SDK to compute the point of intersection, or a least squares approximation to it, for an arbitrary number N of lines in three dimensions. See maxsdk\include\gutil.h.
GEOMEXPORT bool ComputeIntersectionPoint(Point3& intersectionPoint, const Tab<Point3>& fixedCoeffs, const Tab<Point3>& directionVecs, bool doNormalize = true);
Find Node From Modifier APIs
Two new functions have been introduced in the Modifier
class to help find the node an object space modifier is modifying. Often these modifiers need context information available through the node object, such as transform or material related information. These functions neatly encapsulate the not so trivial logic of walking the modifier stack to find the node a given modifier application modifies. For more in depth information, please see the documentation for the following two methods:
CoreExport INode* Modifier::GetNodeFromModData(LocalModData* data, int& index);
CoreExport INode* Modifier::GetNodeFromModContext(ModContext* mc, int& index);
Rendering
ActiveShade in the Viewports
The IActiveShadeFragmentManager
class is for those developers who would like to control the ActiveShade in the viewport layer, or the ActiveShade fragment. This is a layer that is mixed into the viewport representation of the scene. The user can edit the scene while ActiveShade is running and see the results updated in real time in the viewport.
Preview Rendering
Interface::CreatePreview()
now takes two additional optional parameters: a filename string to override the filename specified on the Preview Rendering dialog, and a snippet string that can contain MAXScript code to execute on each frame of the render. The return value from the MAXScript code snippet is displayed in the Preview output.
virtual void CreatePreview(PreviewParams *pvp=NULL, MSTR *filename=NULL, MSTR *snippet=NULL)=0;
3ds Max OSL related APIs
An API has been added to the 3ds Max 2020 SDK, primarily intended for third-party renderer developers. A 3rd party renderer can integrate with 3ds Max's OSL in a few ways. Either the renderer uses the classic C++ shader API, and simply calls Texmap::EvalColor()
, Texmap::EvalMono()
, and Texmap::EvalNormalPerturb()
directly on the OSL map. Or, the it may choose to translate scene properties, shaders, and their parameters to their own OSL implementation. The new OSL API helps in both situations, and aims to solve the following problems:
- Return the set of object properties that the 3ds Max OSL shaders expect to find in the scene, i.e. standard scene attributes like "nodeHandle", "wireColor", and the user-defined attributes from the object property dialog. A third party can either enumerate these and attach to their own scene representation, or directly call these functions at render time to look up the values on the fly.
- Return information about the shader (the source code, compiled bytecode, etc.) and its parameters and connections, so that a renderer with its own OSL implementation can make sure to execute the right code and assign the right parameter set.
- For renderers using the built-in OSL runtime inside 3ds Max, calls to the OSL
trace()
function can now call the third-party renderer by an interface added to theShadeContext
. This allows the optimized ray tracing routines for the third party renderer to be used from within OSL code, even though 3ds Max is executing the shader.
For more information, see the MaxOSLInterface.h header file in the 3ds Max SDK.
Render Element Visibility Opt-in
Before 3ds Max 2020, render elements were visible to the scanline renderer by default.
In 3ds Max 2020, render elements need to explicitly declare themselves visible to the scanline renderer by implementing (overwriting) the following method to return TRUE
:
virtual BOOL MaxRenderElement::IsVisibleToScanlineRenderer()
Miscellaneous
ParamBlock2
id to index mapping
The performance of looking up a parameter index based on its id has been improved. This should impact positively those parameter blocks whose descriptor contains many parameters, such as the parameter block descriptors powering OSL shaders with many parameters.
User Locale Access
To ensure that the same decimal separator is used everywhere in the 3ds Max user interface, we exposed the following function which allows access to a cached value of the user locale 3ds Max acquired from Windows.
//! \brief GetUserLocale retrieves user specific locale specifications
UtilExport const _locale_t& GetUserLocale();
Please note that when exporting numeric values to various data files, it is a best practice to always use "." as the decimal separator.
Archiving
Archiving a scene file has been exposed to the SDK, through the following API:
virtual bool Interface18::ArchiveSceneFile(const MCHAR* archiveFileName,unsigned long saveAsVersion = MAX_RELEASE);
Compression
Setting and getting the scene file compression on save flag has been exposed to the SDK through the following APIs:
virtual bool Interface18::GetSceneFileCompressOnSave();
virtual void Interface18::SetSceneFileCompressOnSave(bool compress, bool persist);
Tablet Support
The TabletManager
class centralizes support for using pressure sensitive devices with 3ds Max.
Notifications
The following new notifications have been added:
//! \brief Sent when ActiveShade in the viewport is toggled on/off.
#define NOTIFY_ACTIVESHADE_IN_VIEWPORT_TOGGLED 0x00000107
//! \brief Sent when potentially starting to shut 3ds Max down, before checking for things that can cancel the shutdown, such as scene dirty or ExitMAXCallback callback object returning false.
//! This notification is sent before NOTIFY_SYSTEM_SHUTDOWN
#define NOTIFY_SYSTEM_SHUTDOWN_CHECK 0x00000108
//! \brief Sent if system shutdown was cancelled. In this case NOTIFY_SYSTEM_SHUTDOWN is not sent.
#define NOTIFY_SYSTEM_SHUTDOWN_CHECK_FAILED 0x00000109
//! \brief Sent if system shutdown was not cancelled, and system shutdown is about to start.
//! This notification is sent before NOTIFY_SYSTEM_SHUTDOWN
#define NOTIFY_SYSTEM_SHUTDOWN_CHECK_PASSED 0x0000010A
//! \brief Sent immediately before MAXScript loads its startup scripts. stdscripts will have already been read.
#define NOTIFY_PRE_MXS_STARTUP_SCRIPT_LOAD 0x00000106
Temporary Object Passing to Non-Const Parameters
In order to make our APIs comply with newer versions of the compiler, we have started to eliminate default parameters of non-trivial types passed by non-const reference.
One example of such an API is:
virtual float CameraObject::GetFOV(TimeValue t, Interval& valid = Interval(0,0))
This API has been changed to
virtual float CameraObject::GetFOV(TimeValue t, Interval& valid)
In order to minimize changes to client code, we also defined the following overload, which will be called any time the first API listed above was called with the default value for its "valid" parameter.
float CameraObject::GetFOV(TimeValue t) { Interval valid(0,0); return GetFOV(t, valid); }
Note that this change should not require code changes in clients of the affected APIs.
New Application Folder Definitions
New application folder for user defined tools, such as MCG graphs or Scene Converter add-ins:
APP_USER_TOOLS_DIR
New application folder for fluid simulations:
APP_FLUID_SIMULATION_DIR
New application folder where user settings files will be placed:
APP_USER_SETTINGS_DIR
Binary Module Version Access
Getting the version of a binary module is now possible by calling the following API:
UtilExport MSTR MaxSDK::Util::GetVersionOfFile(const MCHAR * fileName);
3ds Max Build Number Access
The following API returns the 3ds Max build number as a string of the form "#.#.#.#", where the numbers are the major version, the update version, the hot fix number, and the build number.
UtilExport MSTR MaxSDK::Util::GetMaxBuildNumber();
Floating Point / String Conversion
Transforming between floating point values and strings taking into account the current locale is allowed by these APIs:
CoreExport float GetFloatFromText(const TCHAR* buf, BOOL *valid);
CoreExport TSTR GetTextFromFloat(float f, int precision = 3);
G-buffer ID Change Message
New reference message sent when a node's user defined properties of G-buffer id changed:
REFMSG_NODE_USER_PROPERTY_CHANGED
Deprecated APIs
PARTICLE_SYS_CLASS_ID
has been deprecated. Please use virtual BOOL Object::IsParticleSystem()
to determine if an object is a particle system.
APP_EXCHANGE_STORE_PRIVATE_DIR
has been deprecated. Please use APP_PLUGIN_PACKAGE_PRIVATE_DIR
instead.
APP_EXCHANGE_STORE_PUBLIC_DIR
has been deprecated. Please use APP_PLUGIN_PACKAGE_PUBLIC_DIR
instead.
// Replaced by const MCHAR* PluginPackageManager::GetPublicPackageInstallPath() const;
virtual const MCHAR* IPathConfigMgrEx::GetExchangeStorePlugInInstallPath(const MSTR& productCode)