快速入门:基础
The 源处理器 is a Python script that is executed every time a MAXON API framework or plugin is build. The script checks the code for certain programming errors and creates additional code.
If a project file is created with the Project Tool, the script is automatically added to the build process. It is not needed to run it manually. The output console of the IDE will display which file or files are parsed and what errors the script has detected.
To debug a program and to follow the program flow it is useful to print a statement to the IDE's console window. This can easily be done with DiagnosticOutput() .
For more debug functions see Debug and Output Functions and 调试 .
// This example simply prints "Hello World!" to the debug console.// This example defines a function that prints "Hello World!" to the debug console.
// MAXON API includes // include header files from core.framwork #include " maxon/apibase.h " #include " maxon/string.h "
// ------------------------------------------------------------------------ // Prints "Hello World!" to the console. // ------------------------------------------------------------------------ static void PrintHelloWorld() { const maxon::String world { "World!" }; DiagnosticOutput ( "Hello @" , world); }
The MAXON API includes a sophisticated error system. This system allows functions not only to return arbitrary values but also dedicated error objects. Such an error object contains information on what exactly went wrong within the function. For an overview see Error Handling .
This simple code snippet accesses the name of the host machine:
// get machine data const maxon::DataDictionary data = maxon::Application::GetMachineInfo ();// get machine name const maxon::Result<maxon::String> result = data.Get(maxon::MACHINEINFO::COMPUTERNAME);
maxon::Application::GetMachineInfo() returns a DataDictionary that includes various system data. To access the data one can use maxon::DataDictionaryInterface::Get() . This function does not return a primitive value but a maxon::Result object. Such a maxon::Result object contains either the return value or an error object.
It is possible to manually handle the maxon::Result object. See also Error Result .
// get machine data const maxon::DataDictionary data = maxon::Application::GetMachineInfo ();// get machine name const maxon::Result<maxon::String> result = data.Get(maxon::MACHINEINFO::COMPUTERNAME);
// check if the Result object contains an error or not if (result == maxon::OK ) { // get the value stored in the Result object const maxon::String name = result.GetValue(); DiagnosticOutput ( "Machine Name: @" , name); } else { // get the error stored in the Result object const maxon::Error& err = result. GetError (); DiagnosticOutput ( "Error: @" , err); }
The preferred way of handling errors is to use various attributes. These attributes can be used to handle the program flow in the case of an error. E.g. ifnoerr() executes the given code only if no error occurred.
// get machine data maxon::DataDictionary data = maxon::Application::GetMachineInfo ();// get the machine name ifnoerr ( const maxon::String name = data.Get(maxon::MACHINEINFO::COMPUTERNAME)) { // print the machine name DiagnosticOutput ( "Machine Name: @" , name); }
Within a function that returns a maxon::Result one can return either a valid return value or an error object. Such an error object can be one of many build-in error types. See Error Types .
Within such a function one can also return the error returned by a sub-function. This is automatically done by using the attribute iferr_return . To use this macro one must prepare the scope using iferr_scope .
The attribute iferr_scope_handler allows to define some code that is invoked when an error is detected with iferr_return .
// This example defines a function that uses iferr_scope_handler to handle any internal error.// ------------------------------------------------------------------------ // Prints the machine name to the console. // If an error occurs, the error is printed instead. // ------------------------------------------------------------------------ static void PrintMachineName() { // handle errors iferr_scope_handler { // print error // "err" is defined within iferr_scope_handler DiagnosticOutput ( "Error: @" , err); };
// get application data const maxon::DataDictionary data = maxon::Application::GetMachineInfo ();
// get machine name const maxon::String machineName = data.Get(maxon::MACHINEINFO::COMPUTERNAME) iferr_return ; // print machine name DiagnosticOutput ( "Machine Name: @" , machineName); }
// This example defines a function that uses iferr_scope in order to handle errors using iferr_return.
// core.framework #include " maxon/datadictionary.h " #include " maxon/application.h " #include " maxon/machineinfo.h "
// ------------------------------------------------------------------------ // Compares the machine name with the given name. // @param[in] name A maxon::String to be compared with the machine name. // @return True if the name is equal to the machine name; false if the name is not equal to the machine name. // ------------------------------------------------------------------------ static maxon::Result<maxon::Bool> CompareMachineName( const maxon::String & name) { // needed for "iferr_return" iferr_scope ;
// check if the given string argument is set // return an error if the string is empty if (name. IsEmpty ()) return maxon::IllegalArgumentError( MAXON_SOURCE_LOCATION , "\"name\" argument is empty." _s);
// get the machine name // return an error if the machine name could not be obtained const maxon::DataDictionary data = maxon::Application::GetMachineInfo (); const maxon::String machineName = data.Get(maxon::MACHINEINFO::COMPUTERNAME) iferr_return ;
// return true if the strings are the same if (name.Compare(machineName) == maxon::COMPARERESULT::EQUAL ) return true ; return false ; }
Every plugin that uses the classic API (linking to cinema.framework) must implement the global functions PluginStart() , PluginMessage() and PluginEnd() . This is typically done in a main.cpp file.
PluginStart() is used to register classic API plugins. PluginMessage() can receive various global messages. PluginEnd() is used to free data before the plugin in unloaded when Cinema 4D ends. See 插件函数手册 .
Classic plugins are created by implementing a plugin class e.g. ObjectData or TagData . Such an implementation must be registered in PluginStart() using a specific function like RegisterObjectPlugin() 。见 一般插件信息手册 .
C4DAtom is the base class for many elements of the classic API. It gives access to the element's type, its parameter 描述 , parameter values and dirty state. The class is also used to copy the element and send messages to it. See C4DAtom Manual .
// "cubeObject" is a BaseObject which is based on C4DAtom. // This example checks the object's type, reads a parameter // and copies the object.// get type const Int32 type = cubeObject->GetType(); DiagnosticOutput ( "Type: @" , type);
// get a parameter value GeData data; cubeObject->GetParameter( PRIM_CUBE_LEN , data, DESCFLAGS_GET::NONE ); const ::Vector size = data. GetVector (); DiagnosticOutput ( "Size: @" , size);
// create copy C4DAtom * const clone = cubeObject-> GetClone ( COPYFLAGS::NONE , nullptr ); if (clone == nullptr ) return maxon::OutOfMemoryError( MAXON_SOURCE_LOCATION );
// insert object into the BaseDocument BaseObject * const objectClone = static_cast< BaseObject * > (clone); doc-> InsertObject (objectClone, nullptr , nullptr );
GeListNode is another frequently used base class. It allows to organize elements in lists and trees. It also gives access to the element's document and registration information. See GeListNode Manual .
// "sceneObject" is a BaseObject which is based on GeListNode. // This example accesses the child and next object, the // BaseDocument and registration information on the object.// get child object BaseObject * const childObject = sceneObject-> GetDown (); // get next object BaseObject * const nextObject = sceneObject-> GetNext ();
// get the document BaseDocument * const document = sceneObject-> GetDocument ();
// get info const Int32 info = sceneObject-> GetInfo (); if (info & PLUGINFLAG_HIDEPLUGINMENU ) { DiagnosticOutput ( "Object is hidden in the Plugins menu" ); }
The base class BaseList2D provides functions to handle the element's name, animation tracks, internal data, bits, shaders, layers and IDs. See BaseList2D Manual .
// This example creates a new Material. // The "Material class is based on "BaseList2D", // so it is possible to insert shaders and assign layers.// create a new material 材质 * const newMaterial = Material::Alloc (); if (newMaterial == nullptr ) return maxon::OutOfMemoryError( MAXON_SOURCE_LOCATION );
// insert material doc-> InsertMaterial (newMaterial);
// set name newMaterial-> SetName ( "The new Material" _s);
// make a new shader BaseShader * const noiseShader = BaseShader::Alloc ( Xnoise ); if (noiseShader == nullptr ) return maxon::OutOfMemoryError( MAXON_SOURCE_LOCATION );
// insert shader newMaterial-> InsertShader (noiseShader); newMaterial-> SetParameter ( MATERIAL_COLOR_SHADER , noiseShader, DESCFLAGS_SET::NONE );
// make layer GeListHead * const root = doc-> GetLayerObjectRoot (); if (root == nullptr ) return maxon::UnexpectedError( MAXON_SOURCE_LOCATION ); LayerObject * const newLayer = LayerObject::Alloc (); if (newLayer == nullptr ) return maxon::OutOfMemoryError( MAXON_SOURCE_LOCATION ); newLayer-> SetName ( "New Layer" _s);
// insert layer root-> InsertLast (newLayer);
// set layer newMaterial-> SetLayerObject (newLayer);
The functions C4DAtom::GetParameter() and C4DAtom::SetParameter() allow to access the parameter values of objects, materials, tags etc. The ID of such a parameter is defined using a DescID object. Such a DescID object is composed of multiple DescLevel elements. The complete DescID is also needed when handling an animation track of a given parameter. See DescID Manual .
// This example reads a parameter of the given cube object // and creates an animation track for that parameter.// construct DescID DescID parameterID; parameterID. PushId ( DescLevel ( PRIM_CUBE_LEN , DTYPE_VECTOR , 0)); parameterID. PushId ( DescLevel ( VECTOR_X , DTYPE_REAL , 0));
// get value GeData data; cubeObject->GetParameter(parameterID, data, DESCFLAGS_GET::NONE ); const Float value = data. GetFloat (); DiagnosticOutput ( "Value: @" , value);
// make animation track
// search for the track CTrack * track = cubeObject-> FindCTrack (parameterID); if (track == nullptr ) { // track not found, create new track track = CTrack::Alloc (cubeObject, parameterID); if (track == nullptr ) return maxon::OutOfMemoryError( MAXON_SOURCE_LOCATION ); // add track to stage object cubeObject->InsertTrackSorted(track); }
An interface is a public abstract declaration of a class. Such an interface can be implemented one or multiple times; such implementations are separated from the public interface declaration. This interface system is the base for most MAXON API classes. See MAXON API 接口 .
// This example shows the declaration of a simple interface.// --------------------------------------------------------------------- // Simple class that stores a maxon::Int number. // --------------------------------------------------------------------- class SimpleClassInterface : MAXON_INTERFACE_BASES (maxon::ObjectInterface) { MAXON_INTERFACE (SimpleClassInterface, MAXON_REFERENCE_NORMAL , "net.maxonexample.interfaces.simpleclass" ); public : // --------------------------------------------------------------------- // Sets the number to store. // --------------------------------------------------------------------- MAXON_METHOD void SetNumber( maxon::Int number);
// --------------------------------------------------------------------- // Returns the stored number. // --------------------------------------------------------------------- MAXON_METHOD maxon::Int GetNumber() const ; };
// This interface is declared in a file named "simpleclass.h". The automatically // generated files are therefore named "simpleclass1.hxx" and "simpleclass2.hxx"
// The .hxx header files define the reference class "SimpleClassRef".
#include "simpleclass1.hxx"// declare the published objects "SomeSimpleClass" and "OtherSimpleClass" // that give access to implementations of SimpleClassInterface
// the declaration must be placed between the two hxx files since "SimpleClassRef" is defined in the first hxx file MAXON_DECLARATION ( maxon::Class<SimpleClassRef> , SomeSimpleClass, "net.maxonexample.somesimpleclass" ); MAXON_DECLARATION (SimpleClassRef, OtherSimpleClass, "net.maxonexample.othersimpleclass" ); #include "simpleclass2.hxx"
// This example shows the implementation of a simple interface.
// This class implements SimpleClassInterface. class SimpleClassImplementation : public maxon::Component <SimpleClassImplementation, SimpleClassInterface> { MAXON_COMPONENT (); public : // implementation of interface methods MAXON_METHOD void SetNumber( maxon::Int number) { _value = number; } MAXON_METHOD maxon::Int GetNumber() const { return _value; }
// private data only available inside the implementation private : maxon::Int _value = 1; };
// This example creates an instance of an interface // and uses the created instance.
// define the ID of the component to use const maxon::Id id { "net.maxonexample.class.somesimpleclass" };
// get component class of the given ID from the global maxon::Classes registry const maxon::Class<SimpleClassRef> & componentClass = maxon::Classes::Get<SimpleClassRef>( id );
// create reference const SimpleClassRef simpleClass = componentClass.Create() iferr_return ;
// use reference simpleClass.SetNumber(123); const maxon::Int number = simpleClass.GetNumber(); DiagnosticOutput ( "Number: @" , number);
The MAXON API makes heavy use of reference counting. New instances of a given class are typically handled as reference counted objects. The instance will be freed when all references using that object are deleted. See 参考 .
// This example allocates a new Vector and handles the ownership using a StrongRef. // When the StrongRef object is deleted; the Vector instance gets freed. MAXON_SCOPE { // allocate an object // typically one should not allocate objects this way; this is only an example maxon::Vector * const vec = NewObj ( maxon::Vector ) iferr_return ;// strong reference takes ownership const maxon::StrongRef<maxon::Vector> vectorRef { vec };
// edit referenced object vectorRef-> x = 100.0f; vectorRef->y = 200.0f; vectorRef->z = 300.0f; DiagnosticOutput ( "Vector: @" , vectorRef);
// when the scope is left, the referenced object is deleted }
A registry is used to store implementations of a given interface. Using such a registry it is possible to access all implementations of that interface. See Registries .
// This example prints the IDs of all stream conversion implementations. // These implementations are registered at maxon::StreamConversions.// check all stream conversions for ( const auto & it : maxon::StreamConversions::GetEntriesWithId()) { // get ID const maxon::Id & eid = it.GetKey(); DiagnosticOutput ( "Stream Conversion: @" , eid); }
A published object gives access to a certain object. This can be any kind of object in memory; typically it is a class representing an implementation of an interface. See Published Objects .
// This example uses a specific stream conversion implementation. // An instance of that implementation is created by obtaining // the reference class stored at the published object "HexEncoder".// get encoder factory from published object StreamConversions::HexEncoder // and use it to create a new instance const maxon::StreamConversionRef hexEncoder = maxon::StreamConversions::HexEncoder().Create() iferr_return ;
// the original number const maxon::Int number = 123; maxon::BaseArray<maxon::Char> source; source. Append (( maxon::Char )number) iferr_return ;
// convert to HEX maxon::BaseArray<maxon::Char> destination; hexEncoder.ConvertAll(source, destination) iferr_return ; // print result const maxon::String hexString { destination }; DiagnosticOutput ( "Hex: @" , hexString);