3DCoat  3D-COAT 4.9.xx
3DCoat is the one application that has all the tools you need to take your 3D idea from a block of digital clay all the way to a production ready, fully textured organic or hard surface model.
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
ClassEngine introduction

Class Engine introduction.

Registering classes.

BaseClass used for serialization and UI forms designing without direct UI programming.
You should derive your class from BaseClass. Very typical example of usage:

class MyClass : public BaseClass {
public:
int x;
float y;
cStr s;
}
};

You may separate registration and class definition:

class MyClass : public BaseClass {
public:
int x;
float y;
cStr s;
};
SERIALIZE_NOW(MyClass){
}

Class may be used for UI elements registration and for serialization, see BaseClass::Save, BaseClass::Load
In this case you may defile UI and serialization sections separately

class MyClass : public BaseClass{
public:
void button1();
void button2();
float x,y,z;
//Important! Don't use any heavy checks in this function! It is called frequently. So, use only simple checks there, no any real calculations.
//If you want to create Enumerator there, use convenient _MAKE_ONCE{....} macro.
{//UI registration section
UI_LAYOUT("2");//two columns
FUNCTION_CALL(button1);
FUNCTION_CALL(button2);
}
{//Saving section
}
if(z>0){// you may use c++ conditions in registration section
}
}
};

You may register members conditionally using usual if... else ... commands.
You may use different prefixes to modify registered members. See REG_OPT, INVISIBLE, NOSAVE, RENAME, ets commands.

Usage of the registered classes.

Use BaseClass::Load, BaseClass::Save, BaseClass::SaveBin, BaseClass::LoadBin to serialize.
Use BaseClass::FullCopy to copy BaseClass derived classes.
Use BaseClass::GetElement, BaseClass::FindElement to get access to class members list.
use BaseClass::reset_class to clear registered fields.

Brief summary of commands used for registering inside SERIALIZE section

FUNCTION_CALL(...) - register function call as button.
ICON_BUTTON/3/4(...) - add icon button.
REG_AUTO(variable) - register almost any field by default. Use REG_AUTO(variable, "new_name") to rename variable.
SLIDER(variable, ...) - int or float variable as slider in UI.
REG_TEXTURE(tex) - register integer variable as texture selection control.
FILEPATH(str) - register cStr as control to choose file path.
REG_DROPLIST(fieldID, name, EnumID) - register droplist.
MAKE_ENUMERATOR(ID,List) - create Enumerator.
BCPrintf(...) - printf - like syntax to make own dynamic custom name for the field.
See also REG_RCT(...), REG_VECTOR2D(...), REG_VECTOR3D(...), REG_VECTOR4D(...), REG_MATRIX3D(...), REG_MATRIX4D(...).
_MAKE_ONCE {...} - make some action within brackets once

The UI formatting commands.

UI_LAYOUT(...) - define columns.

Modifiers before the registration of the member

You may use ...MODIFIERS... REG_AUTO(...) or ...MODIFIERS... FUNCTION_CALL(...) to modify how registration works.

CommansDescription
INVISIBLE()An element which invisible in UI, but use in serialization.
READONLY()An unediting element.
NOSAVE()An element which visible in UI, but not use in serialization.
NORESET()Element will not be initialised during reset_class
NONAME()ID of the element is invisible in UI
RENAME(new_name)assign other ID in UI for the control
ICON(path_to_file)Insert icon if control supports. If icon is in material.io/icons/black/ then just write the icon name without extension.
CHK_GROUP(g)specify group for checkbox.
APPLYSCALE()apply scene scale to this control
SKIPHINT()suppress hint for this element
DEFHOTKEY(combination)assign default hotkey to the UI element, like HOTKEY("CTRL E")
LEFTALIGN()force left-align to the control

Examples:

void doit(int p);
...
int x[20];
char c[10];
for(int i=0;i<10;i++){
sprintf_s<20>(c,"x%d",i);
INVISIBLE RENAME(c) REG_AUTO(x[i]);//without RENAME the field will be registered as "x[i]"
}
for(int i=0;i<10;i++){
sprintf_s<20>(c,"button%d",i);
RENAME(c) FUNCTION_CALL(doit, "doit", i);//register 10 buttons: button0 ... button9
}

Additional modifiers of fields names (partially deprecated, use modifiers whenever possible).

You may pass new field name using
RENAME(new_name_there) REG_AUTO(field)
or
REG_AUTO(field, new_name_there)
new_name_there may contain special characters that modify how it is shown/handled in UI. There is the list of modificators -

ValueDescription
{0xHEXADECMAIL_VALUE} set background color.
{fc0xHEXADECMAIL_VALUE} set font color.
[scale=floating_value] set additional scale for the field.
`intger_value checkbox group identifier.
% apply scene scale to this control.
{hotkey hotkey_id} The hotkey will be assigned as default hotkey to the button. Like {hotkey CTRL ALT SHIFT A}
# don't show hint.
~ The function is inaccessible in demo mode.
* mandatory left align.
$ don't show field name in UI.
! hide field, read only.
^ read only field.
{icon filename_path} show icon. If icon is in material.io/icons/black/ then just write the icon name without extension.

Access to the registered class members.

Class members are actually not stored anywhere. The virtual function
BaseClass::CountObjects
used to get dynamic information about the class. But usually you should never call or create it directly. This function is automatically created using SERIALISE() {...} section. You may need to override this function only if you make own container for objects (like ClassArray or ClassPtr).
Usual way to walk through all members of the class looks like:

_EACH(ClassPointer, be){//be is BaseClassElement reference
//write some code there, for example seek for object that contains substring in it's name
//This is actually lambda function if you will look the macro.
//be is reference to BaseClassElement, you may get any ingormation about the field from there.
if(be->ID.IndexOf("MySubString")!=-1){
// return true means exit out of the cycle _EACH ... _EACH_END
return true;
}

You may also get element-wise access, but it is a bit slower, use it only if you don't need to walk through all memebers.

if(ClassPointer->GetElement(be, index, nullptr, nullptr)){
....
}
...or use...
if(ClassPointer->GetElementByName("ID_of_element", nullptr, nullptr, be)){
....
} *

Remember that

TimingOf(Sum_Of_All(Element_Wise_Access)) > TimingOf(Walkig_Using_EACH) > TimingOf(Single_Element_Access) > TimingOf(Walkig_Using_EACH) / Amount_of_elements

Making own container compatible with BaseClass

The good examples of containers are ClassArray, ClassPtr and StringsList
The key point is that you should override functions

virtual void CountObjects(int& required_index, int* counter, DWORD* hash, BaseClassElement* result, const std::function<bool(BaseClassElement*)>* ForEachElem, bool OnlyPointersMode) override;
virtual void Save(TagsList& xml, void* ClassPtr, void* Extra = NULL) override;
virtual bool Load(TagsList& xml,void* ClassPtr,void* Extra = NULL) override;
virtual void SaveBin(void* Data, void* Extra, BinStream* Out, Enumerator* ExDictionary, Enumerator* SkipList) override;
virtual void LoadBin(void* Data, void* Extra, BinStream* In, Enumerator* ExDictionary) override;
virtual void UpdateHash(HashSummator& H, void* data = nullptr, void* extra = nullptr) override;
virtual int ExpandWith(const char* ElmName, void* base) const override;
virtual bool DelElement(int index) override;
virtual void reset_class(void* ptr) override;
virtual bool CheckDirectCasting() const override;
virtual const char* GetClassName() override;
Use AUTONEW() macro within the declaration scope to override new_element() and GetClassSize() automatically.

Deprecated commands.

Previously SAVE_SECTION(mask) and GetClassMask() was extensively used to mask fields. It still works, but should be replaced with realtime checks using if...else... as soon as possible.