利用者:Kwk/Gsoc2010/AutoSelectPaintImage

提供: wiki
移動先: 案内検索

About

I use this page to bring my ideas to paper and to clarify what I am doing.

Scenario

Consider having multiple objects, each with its own set of materials and textures. Let's say, a few of the textures are actually image textures and you want to paint onto them in the UV/Image editor.

Problem

Up until now the UV/Image Editor doesn't care which object is selected nor does it care which texture in the active material's texture slot is active. This makes quick switching of paint textures more complicated than it needs to be.

Idea

Whenever the another texture in the active material's texture slot changes, the image in the UV/Image Editor shall reflect these changes with respect to a setting that indicates if this behavior is wanted or not.

Use Cases

If "Auto Select Image" it toggled on, when will the UV/Image Editor update it's image?

If the user...

  1. explicitly selects another texture slot,
  2. selects another object with a different material

associated with it (implicit material change).

  1. explicitly selects another material.
  2. toggles the "Auto Select Image" option on.

Video

Shows how to enable the "Auto Select Image" feature in the UV/Image Editor.

A video with a feature demonstration can be found here: http://vimeo.com/13215410

Solution

How to change the default behavior of the UV/Image Editor?

The easiest part is to add the state that reflects, if the changes to the UV/Image Editor behavior are wanted or not.

Therefore I've added a boolean RNA property called auto_select_image and connected if to the flag SpaceImage->flag in my branch (soc-2010-kwk).

I chose this location for the SI_AUTO_SELECT_IMAGE flag but it can be moved to a different location if requested by other developers.

/*  selecting the active paint image by active textures in texture stack */
prop= RNA_def_property(srna, "auto_select_image", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", SI_AUTO_SELECT_IMAGE);
RNA_def_property_ui_text(prop, "Auto Select Image", "Automatically select image by active texture in the texture stack");
RNA_def_property_update(prop, NC_SPACE|ND_SPACE_IMAGE, "rna_SpaceImageEditor_auto_select_image_update");

As you can see, the SI_AUTO_SELECT_IMAGE RNA property calls this new function on every update:

static void rna_SpaceImageEditor_auto_select_image_update(Main *bmain, Scene *scene, PointerRNA *ptr)
{	
	/* (kwk) Inform image spaces to update their view if they want to. */
	WM_main_add_notifier(NC_TEXTURE|ND_DATACHANGED, NULL);
}

The rna_SpaceImageEditor_auto_select_image_update function only enqueues a notifier. Now the type of notifier is probably not the best choice since, we didn't actually change the texture but the boolean RNA property. With this update function the fourth use case is fully covered.

How to redraw the image in the image space?

The redraw or update of the image in the image space (UV/Image Editor) is initialized by it's listener function:

static void image_listener(ScrArea *sa, wmNotifier *wmn)
{
	SpaceImage *sima= (SpaceImage *)sa->spacedata.first;
	
	/* context changes */
	switch(wmn->category) {
		case NC_SCENE:
			switch(wmn->data) {
				// ...
				// ...
				// ...
				/* (kwk) Redraw image if another object was selected. */
				case ND_OB_SELECT:
					if(sima->lock && (sima->flag & SI_AUTO_SELECT_IMAGE)) {
						ED_area_tag_refresh(sa);
						ED_area_tag_redraw(sa);
					}
					break;					
			}
			break;
		// ...
		// ...
		// ...
		/* (kwk) Redraw image if active texture index changed. */
		case NC_TEXTURE:
		case NC_MATERIAL:
			switch(wmn->data) {
				default:						
					if(sima->lock && (sima->flag & SI_AUTO_SELECT_IMAGE)) {
						ED_area_tag_refresh(sa);
						ED_area_tag_redraw(sa);
					}
					break;
			}
	}
}

The above code automatically refreshes and redraws the image space area in case there were any changes to the texture or the material or if another object was selected.

The cool thing about the listener function is, that every instance of an image space uses the same listener and voila, we have auto-dispatching.

How to map use cases to the workflow?

It's a bit tricky to map the use cases from above to this workflow.

Use case "1. explicitly selects another texture slot" required some changes to the update callback of the texture slot in rna_material.c.

//...
//...
//...

static void rna_Material_update(Main *bmain, Scene *scene, PointerRNA *ptr)
{
	Material *ma= ptr->id.data;

	DAG_id_flush_update(&ma->id, 0);
	if(scene->gm.matmode == GAME_MAT_GLSL)
		WM_main_add_notifier(NC_MATERIAL|ND_SHADING_DRAW, ma);
	else
		WM_main_add_notifier(NC_MATERIAL|ND_SHADING, ma);
}

static void rna_Material_active_texture_update(Main *bmain, Scene *scene, PointerRNA *ptr)
{
	/* XXX: (kwk) This is a hack to process changes to the active texture index. */
	WM_main_add_notifier(NC_TEXTURE|ND_DATACHANGED, NULL);
	
	rna_Material_update(bmain, scene, ptr);
}

//...
//...
//...

void RNA_def_material(BlenderRNA *brna)
{
	StructRNA *srna;
	PropertyRNA *prop;

	// ...
	// ...
	// ...

	srna= RNA_def_struct(brna, "Material", "ID");
	RNA_def_struct_ui_text(srna, "Material", "Material datablock to defined the appearance of geometric objects for rendering");
	RNA_def_struct_ui_icon(srna, ICON_MATERIAL_DATA);

	// ...
	// ...
	// ...

	/* common */
	rna_def_animdata_common(srna);
	rna_def_mtex_common(srna, "rna_Material_mtex_begin", "rna_Material_active_texture_get", "rna_Material_active_texture_set", "MaterialTextureSlot", "rna_Material_active_texture_update");
}

//...
//...
//...

The I introduced a new update callback called rna_Material_active_texture_update. Normally most of the material RNA properties (including the texture slot) use rna_Material_update' as their update function. My update function is solely for the texture slot and after informing about a change to the texture, it calls the regular rna_Material_update function.

The second and the third use case, required no changes any RNA property. Ony the image space listener needed to be changed to listen to NC_MATERIAL and NC_SCENE|ND_OB_SELECT.

The fourth use case has been covered earlier.

How to select the new image for the image space?

You might wonder that until now the image space hasn't been told to display another image. Well, this is done in the image_refresh function which is called by ED_area_tag_refresh.

static void image_refresh(const bContext *C, ScrArea *sa)
{
	SpaceImage *sima= CTX_wm_space_image(C);

	// ...
	// ...
	// ...
	
	/* (kwk) Now auto select image to display. */	
	if (sima->flag & SI_AUTO_SELECT_IMAGE) {				
		/* Get current object. */
		Object *ob = CTX_data_active_object(C);	
		
		if (ob && ob->type == OB_MESH) {
			/* Get current texture. */
			Tex *tex = give_current_object_texture(ob);			
			
			if (tex && tex->type == TEX_IMAGE && tex->ima) {
				ED_space_image_set(C, sima, CTX_data_scene(C), obedit, tex->ima);	
			}
		}
	}
}

We first check if for this very image space the auto select image option is enabled. Then we get the current object and the texture currently active in the object's material. If the texture is an image texture, we make the image space display the image associated with this texture.