From BlenderWiki

Jump to: navigation, search

Tutorial for Adding a New Object Type to Blender

NOTE: this is nearly finished.

1.0 Introduction

This tutorial covers the basic aspects of adding a new 3D object type to Blender. It is specifically aimed at furthering the integration of Nurbana, but it may also be useful for the up-and-coming animation refactor.

In this tutorial we will be adding a Circle object type to Blender, that allows creating and editing simple linear circular curves.

First, I suggest that any reader read the architecture document at http://www.blender3d.org/cms/Blender_Architecture.336.0.html.

2.0 How Objects Are Stored in Blender

Objects in Blender are stored as special structures that are saved to a file. In the Blender Architecture document is discussed the idea of Library Blocks and a Main tree. Each 3D object type is its own Library Block, and each one reside in its own field in the Main tree.

2.1 The Main Tree

The Main tree is where all savable data is stored, including UI, 3D Objects, packed object data, etc. It's located in "source/blender/blenkernel/BKE_main.h", and contains a linked list base (ListBase) for each Library Block type. This is where we will store the main list of our new 3D objects.

2.2 DNA files

Each savable structure in Blender is preprocessed and must adhere to strict sizing rules. Read this document for more information.

Each preprocessed structure is defined in source/blender/makesdna. This includes screen, object, world, packed data and many other data types.

3.0 Let's Get Started

3.1 Create the DNA File

Ok, now that we have some background information let's get started!

The first thing to do is to create the header file for our new object type's DNA data structure. This will be saved in a file, so put it in "source/blender/makesdna," and call it DNA_circle_types.h. Our 3D circle curve type will allow editing the individual points on the surface. To begin with, it will use the following structures:

typedef struct CircleVert {
    float co[3];
} CircleVert;
 
typedef struct Circle{
    ID id;
    struct CircleVert *verts;
    int totverts;
    int pad;
} Circle;

Next, put #include "DNA_ID.h" at the top of this file, as our Circle structure used a structure called ID. This structure holds information common to all Library Block types in Blender.

Last of all for this file add

#ifndef DNA_CIRCLE_TYPES_H
#define DNA_CIRCLE_TYPES_H

at the very top of the file and

#endif

at the bottom. This is to stop the file being included twice.

Once that's done, the pre-processed include files are referenced directly in "source/blender/makesdna/intern/makesdna.c". Open this file, and add "DNA_circle_types.h", to the end of the _includefiles_ array. Follow the instructions at the bottom of the array and add #include "DNA_circle_types.h" to the bottom of the file.

Rebuild, and everything should work.

tip: If is doesn't rebuild, make sure you have a comma after the entry in the array!

3.2 Create the Necassary C Files

Our circle curve object type will use the following new C source files:

source/blender/blenkernel/intern/circle.c
source/blender/src/drawcircle.c
source/blender/src/editcircle.c

And the following C header files, used for function prototyping:

source/blender/blenkernel/BKE_circle.h
source/blender/include/BIF_drawcircle.h
source/blender/include/BIF_editcircle.h

Add the following #includes to all of those files:

#include "MEM_guardedalloc.h"
 
#include "DNA_circle_types.h"
#include "DNA_ID.h"
#include "DNA_object_types.h"
#include "DNA_listBase.h"
#include "DNA_screen_types.h"
#include "DNA_scene_types.h"
#include "DNA_world_types.h"
 
#include "BIF_interface.h"
 
#include "BKE_main.h"
#include "BKE_global.h"
#include "BKE_object.h"
#include "BKE_library.h"
 
#include "BDR_editobject.h"
 
#include "blendef.h"
 
#include "math.h"

3.3 Creating New Instances of our Object Type

3.3.1 The Allocation Functions

The creation of new Library blocks is handled in special functions in source/blender/blenkernel/library.c. But before we go on, open up source/blender/makesdna/DNA_ID.h. You will find a bunch of #defines beginning with ID_. Add #define ID_CIR MAKE_ID2('C', 'R') to the bottom of it.

Now open up source/blender/blenkernel/BKE_main.h and add ListBase circles to the end of the Main structure.

Now that that's all done, open up source/blender/blenkernel/intern/library.c. Add #include "DNA_circle_types.h" and #include "BKE_circle.h" to the includes file list. Then, find the function wich_libbase, In which you will find a switch statement used to find the ListBase for each particular Library Block type, add

case ID_CIR:
     return &(mainlib->circles);

to the end of it.

Now go to set_listbasepointers and add to to lb array, so lb[27] = &(main->circles); [recognize this?]", and lb[28] = NULL and change return 27 to return 28 (this is important!).

The next function also needs to be added too. Add,

case ID_CIR:
	id = MEM_callocN(sizeof(Circle), "circle");
	break;

to the end of the switch statement. After that, add

case ID_CIR:
	free_circle((Circle *)id);
	break;

to the function "free_libblock" in library.c. Then open blenkernel/intern/circle.c and add

void free_circle(Circle *circle)
{
 
}

Then open blenkernel/BKE_circle.h and add void free_circle(Circle *circle); This will contain code to free the Circle Vertices but not the circle itself.

3.3.1 Actually Making New Objects

3.3.1.1 Adding the Menu Entry

Now in source/blender/src/header_info.c, add #include "BKE_circle.h" to the include files list, then find the function "info_addmenu". Add the following line:

uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Circle", 0, yco-=20, 120, 19, NULL, 0.0, 0.0, 1, 10, "");

Underneath the lattice menu entry. Note that the "10" above is the event return code. Now find "do_info_addmenu," where you will find a switch statement that handles the various menu entries. Add a case clause that calls add_object_draw(OB_CIRCLE_TYPE). Since our menu entry uses event 10, make sure that our case statement does too.

Next open blenkernel/intern/object.c and add

case OB_CIRCLE_TYPE: return add_circle("Circle");

to add_obdata_from_type and

case OB_CIRCLE_TYPE: return "Circle";

to get_obdata_defname just below.

You'll also need to define OB_CIRCLE_TYPE in source/blender/makesdna/DNA_object_types.h. Open up the file, find the right place and add the define, which probably will be 27.

3.3.1.1 Creating a New Circle

Now go make to our function "Add_Circle" in editcircle.c. This function will make a circle with 12 verts, so put the following code into it:

void add_circle_vert(Circle *cir, float x, float y)
{
   cir->totverts++;
   CircleVert *tmp = MEM_callocN(sizeof(CircleVert)*cir->totverts, "CircleVert");
   if(cir->totverts > 1)
   {
       memcpy(tmp, cir->verts, MEM_allocN_len(cir->verts));
   }
   tmp[cir->totverts-1].co[0] = x;
   tmp[cir->totverts-1].co[1] = y;
}
Circle *add_circle(char *name)
{
   Circle *cir = alloc_libblock(&G.main->circles, ID_CIR, name);
   int i;
   float radius = 2.0f;
   for (i = 0; i < 12; i++)
   {
       add_circle_vert(cir, radius * cos(((2*M_PI)/12)*i),radius * sin(((2*M_PI)/12)*i)));
   }
   return cir;
}

And add Circle *add_circle(char *name); to BIF_editcircle.h.

3.4 Drawing our object

Now it's time to make the drawing functions for our circle, as right now all that it drawn is the axis's. -- JosephEagar - 22 Jan 2005