From BlenderWiki

Jump to: navigation, search

Overview

The sculpting tool works as a seperate mode, called Sculpt Mode. I have written a small implementation of this design, and it works fairly well.

Globals

In BKE_global.h:

 struct SculptData* sculptdata;

The sculptdata pointer is initialized on startup and contains all the global sculpting data.

Also in BKE_global.h, a new G.f flag to toggle Sculpt Mode:

 #define G_SCULPTMODE    (1 << 26)

Structs

Defined in BDR_sculptmode.h

 typedef struct BrushData
 {
 	short size, strength;
 	short dir;
 } BrushData;
 
 typedef struct SculptData
 {
 	BrushData drawbrush, smoothbrush, pinchbrush;
 
 	BrushData *currentbrush;
 	short brush_type;
 
 	short symm_x, symm_y, symm_z;
 
 	Mesh *active_mesh;
 
 	ListBase *vertex_users;
 	int vertex_users_size;
 
 	char *face_flags;
 
 	short use_tex;
 	struct Tex *tex;
 	short tex_nr;
 
 	double modelviewmat[16];
 	double projectionmat[16];
 	int viewport[4];
 } SculptData;
 
 /* SculptData brushtype */
 #define DRAW_BRUSH 1
 #define SMOOTH_BRUSH 2
 #define PINCH_BRUSH 3

The BrushData struct simply stores all the settings that can be changed for each brush. Brush settings that are the same for all brushes, like symmetry, are in SculptData.

active_mesh is set whenever the mesh changes, including entering/exiting Sculpt Mode, and when switching from one mesh to another by right clicking.

SculptData also stores vertex_users, which is an array that matches the size of the mvert array in the current Mesh. The elements in the array are lists of the faces that use that vertex. It's necessary to cache this data to speed up some of the calculations.

The face_flags array in SculptData is used to store copies of all the MFace.flag fields.

The three tex fields are used to manipulate the brush shape with a texture.

modelviewmat, projectionmat, and viewport are simply cached values from OpenGl.

Defined in sculptmode.c

 typedef struct IndexNode {
 	struct IndexNode* next,* prev;
 	int Index;
 } IndexNode;
 
 typedef struct ActiveData {
 	unsigned int Index;
 	float Fade;
 } ActiveData;
 
 typedef struct EditData {
 	vec3f center;
 	float size;
 
 	vec3f up, right;
 } EditData;

IndexNode is just used for vertex_users, as an index into the mface array in Mesh.

ActiveData stores an Index into the mvert array in Mesh, and also Fade, which stores how much the brush affects that particular vertex, scaled to the range [0,1].

EditData stores data about the brush. When symmetry is enabled, copies of EditData are made to flip the location (center) and normals (up, right) of the brush.

Functions

All the functions are defined in src/sculptmode.c. init_sculpt_data(), free_sculpt_data(), sculptmode_update_tex(), sculpt(), set_sculptmesh(), and set_sculptmode() are declared in include/BDR_sculptmode.h, but the rest are not, since they aren't used anywhere but sculptmode.c.

Memory/Initialization

init_sculpt_data()

 void init_sculpt_data();

The global data (G.sculptdata) is allocated and initialized in BIF_init() with this function:

free_sculpt_data()

 void free_sculpt_data();

The global data is deleted in exit_usiblender() with this function:

free_vertexusers()

 void free_vertexusers();

There is also a seperate function to free the vertex_user data, since this happens not only at program shutdown, but other times as well:

calc_vertex_users()

 void calc_vertex_users();

Initializes the G.sculptdata->vertex_users array. The array size is Mesh.totvert; each element in the array is a linked list of IndexNodes, which contain an index into the Mesh.mface array. vertex_users can then be used to quickly find all the faces that use a particular vertex.

copy_face_flags()

 void copy_face_flags();

This function is called every time the user enters Sculpt Mode. It's needed so that all the faces in the Mesh can be temporarily set to smooth while sculpting. MFace.flag is copied for each face into the G.sculptdata->face_flags array, then each MFace flag gets ME_SMOOTH added to it.

The reasons for setting all of the faces to smooth is that the calc_damaged_verts() is used to quickly calculate vertex normals, but I think it would be tricky to pass the face normals to the displaylist mesh. And as my mentor pointed out, why would you want to sculpt on an unsmoothed mesh?

restore_face_flags()

 void restore_face_flags();

Called whenever the user exits Sculpt Mode; it simply restores the MFace.flag values to the ones stored in G.sculptdata->face_flags.

set_sculptmesh()

 void set_sculptmesh(Mesh *me);

This function is called any time the active_mesh needs to be set, including setting it to NULL on exiting sculpt mode.

This function also takes care of some initialization and finalization: calc_vertex_users() and copy_face_flags() are called to fill G.sculptdata->vertex_users and G.sculptdata->face_flags, respectively.

On setting active_mesh to NULL, restore_face_flags() is called.

OpenGL

A couple of simple OpenGL-related functions:

init_sculptmatrices()

 void init_sculptmatrices();

Sets modelviewmat, projectionmat, and viewport, which are used for unproject().

get_depth()

 float get_depth(short x, short y);

Uses window coordinates (x,y) to find the depth in the GL depth buffer.

unproject()

 vec3f unproject(const short x, const short y, const float z);

Uses window coordinates (x,y) and depth component z to find a point in model space.

sculptmode_paint_brush()

 
void sculptmode_paint_brush();

Paints a circle around the cursor to show the brush.

Sculpting

brush_strength()

 float brush_strength();

This function combines the brush strength, the brush direction, and a scaling factor to produce a single strength factor for the brush, which controls how strongly, and in which direction, the brush affects vertices.

set_sculptmode()

 void set_sculptmode();

This function does the same thing as set_vpaint(), set_wpaint(), etc: it toggles sculptmode on and off. Whether sculptmode is on or not is controlled by adding G_SCULPTMODE to the G.f flag.

This function calls set_sculptmesh() to do initialization.

sculpt()

 void sculpt();

The interesting stuff starts here. A detailed overview of how it works:

# Push an "undo" with '''BIF_undo_push'''
# [[#init_sculptmatrices()|'''init_sculptmatrices()''']
# Check whether there are modifiers enabled: modifier_calculations= modifiers_getVirtualModifierList(ob) != NULL;
# Go into a loop that will end on mouse up
## [[#init_editdata()|'''init_editdata()''']] is called to set up the brush.
## If modifier_calculations is true, call '''mesh_get_mapped_verts_nors()'''. The mapped data it returns is passed to '''do_symmetrical_brush_actions()'''.
## [[#do_symmetrical_brush_actions()|'''do_symmetrical_brush_actions()''']] is called. 
## If modifier_calculations is true, call '''DAG_object_flush_update()''' to update the normals and to apply the modifiers to the new vertex coordinates. If modifier_calculations is false, call [[#calc_damaged_verts()|'''calc_damaged_verts()''']].
## [[#sculptmode_paint_brush()|'''sculptmode_paint_brush()''']]
## Refresh the screen

init_editdata()

 void init_editdata(EditData *e, short *mouse);

The function sets up EditData. First, the location and size of the brush are scaled to match the size of the model on screen. The reason for this is that setting a brush size of 50 would mean a totally different thing if you had a sphere 100 Blender-Units wide than if the sphere was 1BU. Instead of that, the user selects the brush size (actually, it's the brush radius) in pixels. get_depth() and unproject() are used to convert from view coordinates to model coordinates.

This function also sets the Up and Right normals. These indicate which way 'up' and 'right' are when transformed from view to model coordinates. These are important when using a texture as a brush shape.

do_symmetrical_brush_actions()

 void do_symmetrical_brush_actions(float *vertexcosnos, EditData *e,
 				  ListBase *damaged_verts);

This function calls do_brush_action() at least once. If symmetry is enabled, do_brush_action() is called again, multiple times if necessary. In order to make symmetrical dots, flip_editdata() is used to flip the brush across the model, which then passed directly to do_brush_action().

flip_editdata()

 EditData flip_editdata(EditData *e, short x, short y, short z);

Flips the brush location, plus the two view directions, up and right, across the axes indicated by the values of x, y, and z.

do_brush_action()

 void do_brush_action(float *vertexcosnos, EditData e,
 		     ListBase *damaged_verts);

This function starts by building a linked list of "active_verts", which contains all the vertices that will be modified by the brush, found by taking the distance of each vertex from the center of the brush. The vertices are stored in the list using the ActiveData structure, which contains the Index field for the index of the vertex, plus "Fade", which stores the strength the brush will have on that vertex. For example, using the default settings, a vertex right at the center of the brush would have a Fade of 1, whereas a vertex at the edge would have a Fade of 0.

How Fade is calculated is determined by the G.sculptdata->use_tex flag. If set to false, then a simple cosine curve is used to shape the brush, which give a smooth curve with a gentle falloff. If use_tex is true, then the user is given the option of selecting a texture. This texture is then used to find the strength of the brush for each vertex: tex_strength()

Once the active_verts have been found, the appropriate do_X_brush() function is called. Currently, the available choices are do_draw_brush(), do_smooth_brush(), and do_pinch_brush().

tex_strength()

 float tex_strength(EditData *e, float *point, const float len);

This function uses the texture G.sculptdata->tex to find the strength of the brush for a particular vertex. It does this by converting the 3D location of the vertex into 2D polar coordinates. Basically, the up and right vectors are used to define the 2D plane. The magnitude component is scaled to the size of the image from which the strength will be calculated. The polar coordinates can then be converted to find one pixel in the texture.

It currently makes very limited use of Blender's textures; the texture must contain an image, and that's what's used.

calc_damaged_verts()

 void calc_damaged_verts(ListBase *damaged_verts);

This function loops through all the damaged_verts and, using the vertex_users connectivity data, calculates a new normal for each vertex that has been modified by the last brush action.

Interface

"Sculpt Mode" is added to the mode popup in src/header_view3d. (Specifically, this required changes to view3d_modeselect_pup(), do_view3d_buttons(), and view3d_buttons.)

The interface for Sculpt Mode is created in editing_panel_sculpting_tools(), which is defined in buttons_editing.h. Most of the buttons simply change data in G.sculptdata, but the buttons that switch the brush type have to change a pointer and redraw the editing buttons, so they return the value B_SCULPT_BRUSHTYPE. (At the suggestion of my mentor, I placed this value within the B_FPAINTBUTS section, set to 2860.)

Other changes to the interface: in editing_panel_links(), the Set Smooth/Set Solid buttons are now hidden in Sculpt Mode. (See copy_face_flags() for the explanation.) The transform widget is also deactivated on entering Sculpt Mode.

Problems

There are some problems that need to be resolved with the design

  • The brush doesn't respond to tablet pressure.