2 * ***** BEGIN GPL LICENSE BLOCK *****
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software Foundation,
16 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 * The Original Code is Copyright (C) 2009 Blender Foundation.
19 * All rights reserved.
21 * Contributor(s): Blender Foundation
23 * ***** END GPL LICENSE BLOCK *****
26 /** \file blender/editors/space_buttons/buttons_texture.c
34 #include "MEM_guardedalloc.h"
36 #include "BLI_listbase.h"
37 #include "BLI_string.h"
38 #include "BLI_utildefines.h"
40 #include "BLF_translation.h"
42 #include "DNA_brush_types.h"
44 #include "DNA_lamp_types.h"
45 #include "DNA_material_types.h"
46 #include "DNA_node_types.h"
47 #include "DNA_object_types.h"
48 #include "DNA_object_force.h"
49 #include "DNA_particle_types.h"
50 #include "DNA_scene_types.h"
51 #include "DNA_screen_types.h"
52 #include "DNA_space_types.h"
53 #include "DNA_world_types.h"
54 #include "DNA_linestyle_types.h"
57 #include "BKE_context.h"
58 #include "BKE_material.h"
59 #include "BKE_modifier.h"
61 #include "BKE_paint.h"
62 #include "BKE_particle.h"
63 #include "BKE_scene.h"
64 #include "BKE_freestyle.h"
66 #include "RNA_access.h"
68 #include "UI_interface.h"
69 #include "UI_resources.h"
71 #include "ED_buttons.h"
73 #include "ED_screen.h"
75 #include "../interface/interface_intern.h"
77 #include "buttons_intern.h" // own include
79 /****************** "Old Shading" Texture Context ****************/
81 bool ED_texture_context_check_world(const bContext *C)
83 Scene *scene = CTX_data_scene(C);
84 return (scene && scene->world);
87 bool ED_texture_context_check_material(const bContext *C)
89 Object *ob = CTX_data_active_object(C);
90 return (ob && (ob->totcol != 0));
93 bool ED_texture_context_check_lamp(const bContext *C)
95 Object *ob = CTX_data_active_object(C);
96 return (ob && (ob->type == OB_LAMP));
99 bool ED_texture_context_check_particles(const bContext *C)
101 Object *ob = CTX_data_active_object(C);
102 return (ob && ob->particlesystem.first);
105 bool ED_texture_context_check_linestyle(const bContext *C)
107 #ifdef WITH_FREESTYLE
108 Scene *scene = CTX_data_scene(C);
109 SceneRenderLayer *actsrl;
110 FreestyleConfig *config;
111 FreestyleLineSet *lineset;
112 FreestyleLineStyle *linestyle;
114 if (scene && (scene->r.mode & R_EDGE_FRS)) {
115 actsrl = BLI_findlink(&scene->r.layers, scene->r.actlay);
116 config = &actsrl->freestyleConfig;
117 if (config->mode == FREESTYLE_CONTROL_EDITOR_MODE) {
118 lineset = BKE_freestyle_lineset_get_active(config);
120 linestyle = lineset->linestyle;
121 return linestyle && (linestyle->flag & LS_TEXTURE);
129 static void texture_context_check_modifier_foreach(void *userData, Object *UNUSED(ob), ModifierData *UNUSED(md),
130 const char *UNUSED(propname))
132 *((bool *)userData) = true;
135 bool ED_texture_context_check_others(const bContext *C)
137 /* We cannot rely on sbuts->texuser here, as it is NULL when in "old" tex handling, non-OTHERS tex context. */
138 Object *ob = CTX_data_active_object(C);
142 /* Tex force field. */
143 if (ob->pd && ob->pd->forcefield == PFIELD_TEXTURE) {
150 modifiers_foreachTexLink(ob, texture_context_check_modifier_foreach, &check);
158 if (BKE_paint_brush(BKE_paint_get_active_from_context(C))) {
165 static void set_texture_context(const bContext *C, SpaceButs *sbuts)
167 Scene *scene = CTX_data_scene(C);
169 if (BKE_scene_use_new_shading_nodes(scene)) {
170 return; /* No texture context in new shading mode */
174 bool valid_world = ED_texture_context_check_world(C);
175 bool valid_material = ED_texture_context_check_material(C);
176 bool valid_lamp = ED_texture_context_check_lamp(C);
177 bool valid_particles = ED_texture_context_check_particles(C);
178 bool valid_linestyle = ED_texture_context_check_linestyle(C);
179 bool valid_others = ED_texture_context_check_others(C);
181 /* this is similar to direct user action, no need to keep "better" ctxt in _prev */
182 if ((sbuts->mainb == BCONTEXT_WORLD) && valid_world) {
183 sbuts->texture_context = sbuts->texture_context_prev = SB_TEXC_WORLD;
185 else if ((sbuts->mainb == BCONTEXT_MATERIAL) && valid_material) {
186 sbuts->texture_context = sbuts->texture_context_prev = SB_TEXC_MATERIAL;
188 else if ((sbuts->mainb == BCONTEXT_DATA) && valid_lamp) {
189 sbuts->texture_context = sbuts->texture_context_prev = SB_TEXC_LAMP;
191 else if ((sbuts->mainb == BCONTEXT_PARTICLE) && valid_particles) {
192 sbuts->texture_context = sbuts->texture_context_prev = SB_TEXC_PARTICLES;
194 else if ((sbuts->mainb == BCONTEXT_RENDER_LAYER) && valid_linestyle) {
195 sbuts->texture_context = sbuts->texture_context_prev = SB_TEXC_LINESTYLE;
197 else if ((ELEM(sbuts->mainb, BCONTEXT_MODIFIER, BCONTEXT_PHYSICS)) && valid_others) {
198 sbuts->texture_context = sbuts->texture_context_prev = SB_TEXC_OTHER;
200 /* Else, try to revive a previous "better" ctxt... */
201 else if ((sbuts->texture_context_prev != sbuts->texture_context) &&
202 (((sbuts->texture_context_prev == SB_TEXC_WORLD) && valid_world) ||
203 ((sbuts->texture_context_prev == SB_TEXC_MATERIAL) && valid_material) ||
204 ((sbuts->texture_context_prev == SB_TEXC_LAMP) && valid_lamp) ||
205 ((sbuts->texture_context_prev == SB_TEXC_PARTICLES) && valid_particles) ||
206 ((sbuts->texture_context_prev == SB_TEXC_LINESTYLE) && valid_linestyle) ||
207 ((sbuts->texture_context_prev == SB_TEXC_OTHER) && valid_others)))
209 sbuts->texture_context = sbuts->texture_context_prev;
211 /* Else, just be sure that current context is valid! */
212 else if (((sbuts->texture_context == SB_TEXC_WORLD) && !valid_world) ||
213 ((sbuts->texture_context == SB_TEXC_MATERIAL) && !valid_material) ||
214 ((sbuts->texture_context == SB_TEXC_LAMP) && !valid_lamp) ||
215 ((sbuts->texture_context == SB_TEXC_PARTICLES) && !valid_particles) ||
216 ((sbuts->texture_context == SB_TEXC_LINESTYLE) && !valid_linestyle) ||
217 ((sbuts->texture_context == SB_TEXC_OTHER) && !valid_others))
219 /* this is default fallback, do keep "better" ctxt in _prev */
220 sbuts->texture_context_prev = sbuts->texture_context;
221 if (valid_material) {
222 sbuts->texture_context = SB_TEXC_MATERIAL;
224 else if (valid_lamp) {
225 sbuts->texture_context = SB_TEXC_LAMP;
227 else if (valid_particles) {
228 sbuts->texture_context = SB_TEXC_PARTICLES;
230 else if (valid_linestyle) {
231 sbuts->texture_context = SB_TEXC_LINESTYLE;
233 else if (valid_world) {
234 sbuts->texture_context = SB_TEXC_WORLD;
236 else if (valid_others) {
237 sbuts->texture_context = SB_TEXC_OTHER;
243 /************************* Texture User **************************/
245 static void buttons_texture_user_property_add(ListBase *users, ID *id,
246 PointerRNA ptr, PropertyRNA *prop,
247 const char *category, int icon, const char *name)
249 ButsTextureUser *user = MEM_callocN(sizeof(ButsTextureUser), "ButsTextureUser");
254 user->category = category;
257 user->index = BLI_countlist(users);
259 BLI_addtail(users, user);
262 static void buttons_texture_user_node_add(ListBase *users, ID *id,
263 bNodeTree *ntree, bNode *node,
264 const char *category, int icon, const char *name)
266 ButsTextureUser *user = MEM_callocN(sizeof(ButsTextureUser), "ButsTextureUser");
271 user->category = category;
274 user->index = BLI_countlist(users);
276 BLI_addtail(users, user);
279 static void buttons_texture_users_find_nodetree(ListBase *users, ID *id,
280 bNodeTree *ntree, const char *category)
285 for (node = ntree->nodes.first; node; node = node->next) {
286 if (node->typeinfo->nclass == NODE_CLASS_TEXTURE) {
288 /* PropertyRNA *prop; */ /* UNUSED */
290 RNA_pointer_create(&ntree->id, &RNA_Node, node, &ptr);
291 /* prop = RNA_struct_find_property(&ptr, "texture"); */ /* UNUSED */
293 buttons_texture_user_node_add(users, id, ntree, node,
294 category, RNA_struct_ui_icon(ptr.type), node->name);
296 else if (node->type == NODE_GROUP && node->id) {
297 buttons_texture_users_find_nodetree(users, id, (bNodeTree *)node->id, category);
303 static void buttons_texture_modifier_foreach(void *userData, Object *ob, ModifierData *md, const char *propname)
307 ListBase *users = userData;
309 RNA_pointer_create(&ob->id, &RNA_Modifier, md, &ptr);
310 prop = RNA_struct_find_property(&ptr, propname);
312 buttons_texture_user_property_add(users, &ob->id, ptr, prop,
313 "Modifiers", RNA_struct_ui_icon(ptr.type), md->name);
316 static void buttons_texture_users_from_context(ListBase *users, const bContext *C, SpaceButs *sbuts)
323 FreestyleLineStyle *linestyle = NULL;
325 ID *pinid = sbuts->pinid;
326 bool limited_mode = (sbuts->flag & SB_TEX_USER_LIMITED) != 0;
328 /* get data from context */
330 if (GS(pinid->name) == ID_SCE)
331 scene = (Scene *)pinid;
332 else if (GS(pinid->name) == ID_OB)
333 ob = (Object *)pinid;
334 else if (GS(pinid->name) == ID_LA)
336 else if (GS(pinid->name) == ID_WO)
337 wrld = (World *)pinid;
338 else if (GS(pinid->name) == ID_MA)
339 ma = (Material *)pinid;
340 else if (GS(pinid->name) == ID_BR)
341 brush = (Brush *)pinid;
342 else if (GS(pinid->name) == ID_LS)
343 linestyle = (FreestyleLineStyle *)pinid;
347 scene = CTX_data_scene(C);
349 if (!(pinid || pinid == &scene->id)) {
350 ob = (scene->basact) ? scene->basact->object : NULL;
352 brush = BKE_paint_brush(BKE_paint_get_active_from_context(C));
353 linestyle = CTX_data_linestyle_from_scene(scene);
356 if (ob && ob->type == OB_LAMP && !la)
359 ma = give_current_material(ob, ob->actcol);
362 BLI_listbase_clear(users);
364 if (ma && !limited_mode)
365 buttons_texture_users_find_nodetree(users, &ma->id, ma->nodetree, "Material");
366 if (la && !limited_mode)
367 buttons_texture_users_find_nodetree(users, &la->id, la->nodetree, "Lamp");
368 if (wrld && !limited_mode)
369 buttons_texture_users_find_nodetree(users, &wrld->id, wrld->nodetree, "World");
370 if (linestyle && !limited_mode)
371 buttons_texture_users_find_nodetree(users, &linestyle->id, linestyle->nodetree, "LineStyle");
374 ParticleSystem *psys = psys_get_current(ob);
379 modifiers_foreachTexLink(ob, buttons_texture_modifier_foreach, users);
381 /* particle systems */
382 if (psys && !limited_mode) {
383 for (a = 0; a < MAX_MTEX; a++) {
384 mtex = psys->part->mtex[a];
390 RNA_pointer_create(&psys->part->id, &RNA_ParticleSettingsTextureSlot, mtex, &ptr);
391 prop = RNA_struct_find_property(&ptr, "texture");
393 buttons_texture_user_property_add(users, &psys->part->id, ptr, prop,
394 "Particles", RNA_struct_ui_icon(&RNA_ParticleSettings), psys->name);
400 if (ob->pd && ob->pd->forcefield == PFIELD_TEXTURE) {
404 RNA_pointer_create(&ob->id, &RNA_FieldSettings, ob->pd, &ptr);
405 prop = RNA_struct_find_property(&ptr, "texture");
407 buttons_texture_user_property_add(users, &ob->id, ptr, prop,
408 "Fields", ICON_FORCE_TEXTURE, "Texture Field");
418 RNA_pointer_create(&brush->id, &RNA_BrushTextureSlot, &brush->mtex, &ptr);
419 prop = RNA_struct_find_property(&ptr, "texture");
421 buttons_texture_user_property_add(users, &brush->id, ptr, prop,
422 "Brush", ICON_BRUSH_DATA, "Brush");
425 RNA_pointer_create(&brush->id, &RNA_BrushTextureSlot, &brush->mask_mtex, &ptr);
426 prop = RNA_struct_find_property(&ptr, "texture");
428 buttons_texture_user_property_add(users, &brush->id, ptr, prop,
429 "Brush", ICON_BRUSH_DATA, "Brush Mask");
433 void buttons_texture_context_compute(const bContext *C, SpaceButs *sbuts)
435 /* gather available texture users in context. runs on every draw of
436 * properties editor, before the buttons are created. */
437 ButsContextTexture *ct = sbuts->texuser;
438 Scene *scene = CTX_data_scene(C);
439 ID *pinid = sbuts->pinid;
441 set_texture_context(C, sbuts);
443 if (!(BKE_scene_use_new_shading_nodes(scene) || (sbuts->texture_context == SB_TEXC_OTHER))) {
445 BLI_freelistN(&ct->users);
447 sbuts->texuser = NULL;
454 ct = MEM_callocN(sizeof(ButsContextTexture), "ButsContextTexture");
458 BLI_freelistN(&ct->users);
461 buttons_texture_users_from_context(&ct->users, C, sbuts);
463 if (pinid && GS(pinid->name) == ID_TE) {
465 ct->texture = (Tex *)pinid;
468 /* set one user as active based on active index */
469 if (ct->index >= BLI_countlist(&ct->users))
472 ct->user = BLI_findlink(&ct->users, ct->index);
476 if (ct->user->ptr.data) {
480 /* get texture datablock pointer if it's a property */
481 texptr = RNA_property_pointer_get(&ct->user->ptr, ct->user->prop);
482 tex = (RNA_struct_is_a(texptr.type, &RNA_Texture)) ? texptr.data : NULL;
486 else if (ct->user->node && !(ct->user->node->flag & NODE_ACTIVE_TEXTURE)) {
487 ButsTextureUser *user;
489 /* detect change of active texture node in same node tree, in that
490 * case we also automatically switch to the other node */
491 for (user = ct->users.first; user; user = user->next) {
492 if (user->ntree == ct->user->ntree && user->node != ct->user->node) {
493 if (user->node->flag & NODE_ACTIVE_TEXTURE) {
495 ct->index = BLI_findindex(&ct->users, user);
505 static void template_texture_select(bContext *C, void *user_p, void *UNUSED(arg))
507 /* callback when selecting a texture user in the menu */
508 SpaceButs *sbuts = CTX_wm_space_buts(C);
509 ButsContextTexture *ct = (sbuts) ? sbuts->texuser : NULL;
510 ButsTextureUser *user = (ButsTextureUser *)user_p;
517 /* set user as active */
519 ED_node_set_active(CTX_data_main(C), user->ntree, user->node);
523 texptr = RNA_property_pointer_get(&user->ptr, user->prop);
524 tex = (RNA_struct_is_a(texptr.type, &RNA_Texture)) ? texptr.data : NULL;
528 if (user->ptr.type == &RNA_ParticleSettingsTextureSlot) {
529 /* stupid exception for particle systems which still uses influence
530 * from the old texture system, set the active texture slots as well */
531 ParticleSettings *part = user->ptr.id.data;
534 for (a = 0; a < MAX_MTEX; a++)
535 if (user->ptr.data == part->mtex[a])
541 ct->index = user->index;
544 static void template_texture_user_menu(bContext *C, uiLayout *layout, void *UNUSED(arg))
546 /* callback when opening texture user selection menu, to create buttons. */
547 SpaceButs *sbuts = CTX_wm_space_buts(C);
548 ButsContextTexture *ct = sbuts->texuser;
549 ButsTextureUser *user;
550 uiBlock *block = uiLayoutGetBlock(layout);
551 const char *last_category = NULL;
553 for (user = ct->users.first; user; user = user->next) {
555 char name[UI_MAX_NAME_STR];
557 /* add label per category */
558 if (!last_category || strcmp(last_category, user->category) != 0) {
559 uiItemL(layout, user->category, ICON_NONE);
560 but = block->buttons.last;
561 but->drawflag = UI_BUT_TEXT_LEFT;
566 PointerRNA texptr = RNA_property_pointer_get(&user->ptr, user->prop);
567 Tex *tex = texptr.data;
570 BLI_snprintf(name, UI_MAX_NAME_STR, " %s - %s", user->name, tex->id.name + 2);
572 BLI_snprintf(name, UI_MAX_NAME_STR, " %s", user->name);
575 BLI_snprintf(name, UI_MAX_NAME_STR, " %s", user->name);
577 but = uiDefIconTextBut(block, BUT, 0, user->icon, name, 0, 0, UI_UNIT_X * 4, UI_UNIT_Y,
578 NULL, 0.0, 0.0, 0.0, 0.0, "");
579 uiButSetNFunc(but, template_texture_select, MEM_dupallocN(user), NULL);
581 last_category = user->category;
585 void uiTemplateTextureUser(uiLayout *layout, bContext *C)
587 /* texture user selection dropdown menu. the available users have been
588 * gathered before drawing in ButsContextTexture, we merely need to
589 * display the current item. */
590 SpaceButs *sbuts = CTX_wm_space_buts(C);
591 ButsContextTexture *ct = (sbuts) ? sbuts->texuser : NULL;
592 uiBlock *block = uiLayoutGetBlock(layout);
594 ButsTextureUser *user;
595 char name[UI_MAX_NAME_STR];
600 /* get current user */
604 uiItemL(layout, IFACE_("No textures in context"), ICON_NONE);
609 BLI_strncpy(name, user->name, UI_MAX_NAME_STR);
612 but = uiDefIconTextMenuBut(block, template_texture_user_menu, NULL,
613 user->icon, name, 0, 0, UI_UNIT_X * 4, UI_UNIT_Y, "");
616 but = uiDefMenuBut(block, template_texture_user_menu, NULL,
617 name, 0, 0, UI_UNIT_X * 4, UI_UNIT_Y, "");
620 /* some cosmetic tweaks */
621 uiButSetMenuFromPulldown(but);
623 but->flag &= ~UI_ICON_SUBMENU;
626 /************************* Texture Show **************************/
628 static void template_texture_show(bContext *C, void *data_p, void *prop_p)
630 SpaceButs *sbuts = CTX_wm_space_buts(C);
631 ButsContextTexture *ct = (sbuts) ? sbuts->texuser : NULL;
632 ButsTextureUser *user;
637 for (user = ct->users.first; user; user = user->next)
638 if (user->ptr.data == data_p && user->prop == prop_p)
643 template_texture_select(C, user, NULL);
646 sbuts->mainb = BCONTEXT_TEXTURE;
647 sbuts->mainbuser = sbuts->mainb;
651 ED_area_tag_redraw(CTX_wm_area(C));
655 void uiTemplateTextureShow(uiLayout *layout, bContext *C, PointerRNA *ptr, PropertyRNA *prop)
657 /* button to quickly show texture in texture tab */
658 SpaceButs *sbuts = CTX_wm_space_buts(C);
659 ButsContextTexture *ct = (sbuts) ? sbuts->texuser : NULL;
660 ButsTextureUser *user;
662 /* only show button in other tabs in properties editor */
663 if (!ct || sbuts->mainb == BCONTEXT_TEXTURE)
666 /* find corresponding texture user */
667 for (user = ct->users.first; user; user = user->next)
668 if (user->ptr.data == ptr->data && user->prop == prop)
673 uiBlock *block = uiLayoutGetBlock(layout);
676 but = uiDefIconBut(block, BUT, 0, ICON_BUTS, 0, 0, UI_UNIT_X, UI_UNIT_Y,
677 NULL, 0.0, 0.0, 0.0, 0.0, "Show texture in texture tab");
678 uiButSetFunc(but, template_texture_show, user->ptr.data, user->prop);