I am writing this because some time ago I took a look inside the source code of the most popular 3D open source computer graphics software called "Blender 3D". I didn't have a blog at that time and I feel like I learned a lot just by studying the source code of this program.
To start things, Blender is actually written in C mostly with some parts in C++ and some parts in Python. To make matters a bit more confusing, the parts written in C actually represent the MVC design pattern. Yes, I know. C isn't really an object oriented programming language and yet here we have an application written in C that implements the MVC design pattern.
So how does it implement the MVC design pattern? The application is separated into 3 main areas.
The first area contains the operator and operator types. An operator type pretty much describes any action a user can execute inside the program that alters the data in the program. The operator represents an actual instance of an operator type and includes all the data from the curent execution of the operator type. An operator can be execute instantly or over a given duration. It is really important in 3D programs that the user can start an action and keep it active over a period of time during which he makes adjustments to that action while seeing the results in real time.
During the execution of an operator type, it listens for user input and updates the edited 3d models in real time. Then once the user is happy with the changes, he can finish the action.
So each operator type will the following functions:
To start things, Blender is actually written in C mostly with some parts in C++ and some parts in Python. To make matters a bit more confusing, the parts written in C actually represent the MVC design pattern. Yes, I know. C isn't really an object oriented programming language and yet here we have an application written in C that implements the MVC design pattern.
So how does it implement the MVC design pattern? The application is separated into 3 main areas.
The first area contains the operator and operator types. An operator type pretty much describes any action a user can execute inside the program that alters the data in the program. The operator represents an actual instance of an operator type and includes all the data from the curent execution of the operator type. An operator can be execute instantly or over a given duration. It is really important in 3D programs that the user can start an action and keep it active over a period of time during which he makes adjustments to that action while seeing the results in real time.
During the execution of an operator type, it listens for user input and updates the edited 3d models in real time. Then once the user is happy with the changes, he can finish the action.
So each operator type will the following functions:
- A function called "poll" to check the current context inside the application to see if it meets the necessary conditions for the operator to be executed.
- A function called "invoke" to execute the operator or to start the execution of the operator over a period of time
- A function called "modal" to update the operator periodically in case it is executed over a period of time
- A function called "cancel" to cancel the execution
- And finally, a function called "execute" to execute the operator without user input
The definition of an operator type in C looks like this:
The definition of the operator, an actual execution of the operator type looks like this:
As you can see, in the operator we store the data corresponding to current run of the operator type.
And inside the operator type we can see some pointers to functions that I listed above.
So these operators are pretty much the controllers inside the application that change the model and then notifies the user interface to update itself.
The models in the program are what contains the representation of all the data inside the application. They feed the user interface with things to show. They are declared as simple C structures for an important reason. A data access layer written and generated in C is used by python to access those models. This is actually an ORM written in C for python to access the data in the C structures. It's another case of "impedance mismatch".
So each C structure has a corresponding mapping for the python scripting engine. For example, if a structure has a set of fields then for each field we have C function for getting the value of the field or setting the value of a field given the name of the field. When changing a field from a structure there is also a way to notify that the value has been changed to update the rest of the blender data which is called the "scene" data. For example an object can have a series of modifiers that changes the appearance of the object. Each of them has a corresponding C structure. An user can change some fields inside that C structure which should updated the object that the modifier is applied on.
This is how a C structure corresponding to the modifiers mentioned above looks like:
It actually implements some primitive C inheritances. You can nest a structure inside another structure at the beginning of it.
The corresponding mapping looks like this:
You can see that is has a name that corresponds to the name of the structure in C. For each of these structure instances, a python wrapper is generated containing a pointer to the C structure and the name of the structure. The name is used to search the global hash for the corresponding C mapping. Then the mapping, like the one shown above, it is used to read and write data inside the C structure.
Bellow you can see the mapping of a property or field of a structure.
The mappings aren't 100% exactly the same as the structure. In this case it just sets a bit inside the "mode" field in the modifier data. This can enable or disable the modifier in the various view modes inside blender.
The structure mapping contains a collection of property/field mappings like the one shown above. Bellow you can see the collection of properties/fields of the Subdivision modifier used to acces fields or properties from the C structure:
You can see that it has some functions to iterate through the fields/properties mappings and to fetch a property/field mapping.
Finally there is the user interface which represents the "view" from the MVC pattern. In the case of Blender, the view is mostly written in python. Python scripts are called from the main drawing function written in C. For those python scripts, a series of wrappers are generated for some C functions used to layout and draw various components. The python scripts are used mostly to generate the layout of the user interface. For example, in the case of the modifier it is used to generate a list of the properties of the modifier.
You can see in the screenshot bellow, how panel with the options for the Subsurface modifier is drawn and displayed:
This is about all that I can say about Blender in this post. I actually omitted half the information that I wanted to share. Maybe I will make another post about the omitted things.
As you can see, in the operator we store the data corresponding to current run of the operator type.
And inside the operator type we can see some pointers to functions that I listed above.
So these operators are pretty much the controllers inside the application that change the model and then notifies the user interface to update itself.
The models in the program are what contains the representation of all the data inside the application. They feed the user interface with things to show. They are declared as simple C structures for an important reason. A data access layer written and generated in C is used by python to access those models. This is actually an ORM written in C for python to access the data in the C structures. It's another case of "impedance mismatch".
So each C structure has a corresponding mapping for the python scripting engine. For example, if a structure has a set of fields then for each field we have C function for getting the value of the field or setting the value of a field given the name of the field. When changing a field from a structure there is also a way to notify that the value has been changed to update the rest of the blender data which is called the "scene" data. For example an object can have a series of modifiers that changes the appearance of the object. Each of them has a corresponding C structure. An user can change some fields inside that C structure which should updated the object that the modifier is applied on.
This is how a C structure corresponding to the modifiers mentioned above looks like:
It actually implements some primitive C inheritances. You can nest a structure inside another structure at the beginning of it.
The corresponding mapping looks like this:
You can see that is has a name that corresponds to the name of the structure in C. For each of these structure instances, a python wrapper is generated containing a pointer to the C structure and the name of the structure. The name is used to search the global hash for the corresponding C mapping. Then the mapping, like the one shown above, it is used to read and write data inside the C structure.
Bellow you can see the mapping of a property or field of a structure.
The mappings aren't 100% exactly the same as the structure. In this case it just sets a bit inside the "mode" field in the modifier data. This can enable or disable the modifier in the various view modes inside blender.
The structure mapping contains a collection of property/field mappings like the one shown above. Bellow you can see the collection of properties/fields of the Subdivision modifier used to acces fields or properties from the C structure:
You can see that it has some functions to iterate through the fields/properties mappings and to fetch a property/field mapping.
Finally there is the user interface which represents the "view" from the MVC pattern. In the case of Blender, the view is mostly written in python. Python scripts are called from the main drawing function written in C. For those python scripts, a series of wrappers are generated for some C functions used to layout and draw various components. The python scripts are used mostly to generate the layout of the user interface. For example, in the case of the modifier it is used to generate a list of the properties of the modifier.
You can see in the screenshot bellow, how panel with the options for the Subsurface modifier is drawn and displayed:
This is about all that I can say about Blender in this post. I actually omitted half the information that I wanted to share. Maybe I will make another post about the omitted things.
Comments
Post a Comment