Bridge edge loops would loose all edge flags.
[blender.git] / source / blender / editors / mesh / mesh_data.c
1 /**
2  * $Id$
3  *
4  * ***** BEGIN GPL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version. 
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19  *
20  * The Original Code is Copyright (C) 2009 Blender Foundation.
21  * All rights reserved.
22  *
23  * 
24  * Contributor(s): Blender Foundation
25  *
26  * ***** END GPL LICENSE BLOCK *****
27  */
28
29 #include <math.h>
30 #include <stdlib.h>
31 #include <string.h>
32
33 #include "MEM_guardedalloc.h"
34
35 #include "DNA_customdata_types.h"
36 #include "DNA_material_types.h"
37 #include "DNA_mesh_types.h"
38 #include "DNA_meshdata_types.h"
39 #include "DNA_object_types.h"
40 #include "DNA_scene_types.h"
41 #include "DNA_windowmanager_types.h"
42
43 #include "BKE_context.h"
44 #include "BKE_customdata.h"
45 #include "BKE_depsgraph.h"
46 #include "BKE_displist.h"
47 #include "BKE_global.h"
48 #include "BKE_material.h"
49 #include "BKE_mesh.h"
50 #include "BKE_report.h"
51
52 #include "BLI_arithb.h"
53 #include "BLI_editVert.h"
54 #include "BLI_edgehash.h"
55
56 #include "RNA_access.h"
57 #include "RNA_define.h"
58
59 #include "WM_api.h"
60 #include "WM_types.h"
61
62 #include "ED_mesh.h"
63 #include "ED_object.h"
64 #include "ED_view3d.h"
65
66 #include "mesh_intern.h"
67
68 static void delete_customdata_layer(bContext *C, Object *ob, CustomDataLayer *layer)
69 {
70         Mesh *me = ob->data;
71         CustomData *data= (me->edit_mesh)? &me->edit_mesh->fdata: &me->fdata;
72         void *actlayerdata, *rndlayerdata, *clonelayerdata, *masklayerdata, *layerdata=layer->data;
73         int type= layer->type;
74         int index= CustomData_get_layer_index(data, type);
75         int i, actindex, rndindex, cloneindex, maskindex;
76         
77         /* ok, deleting a non-active layer needs to preserve the active layer indices.
78           to do this, we store a pointer to the .data member of both layer and the active layer,
79           (to detect if we're deleting the active layer or not), then use the active
80           layer data pointer to find where the active layer has ended up.
81           
82           this is necassary because the deletion functions only support deleting the active
83           layer. */
84         actlayerdata = data->layers[CustomData_get_active_layer_index(data, type)].data;
85         rndlayerdata = data->layers[CustomData_get_render_layer_index(data, type)].data;
86         clonelayerdata = data->layers[CustomData_get_clone_layer_index(data, type)].data;
87         masklayerdata = data->layers[CustomData_get_mask_layer_index(data, type)].data;
88         CustomData_set_layer_active(data, type, layer - &data->layers[index]);
89
90         if(me->edit_mesh) {
91                 EM_free_data_layer(me->edit_mesh, data, type);
92         }
93         else {
94                 CustomData_free_layer_active(data, type, me->totface);
95                 mesh_update_customdata_pointers(me);
96         }
97
98         if(!CustomData_has_layer(data, type) && (type == CD_MCOL && (ob->mode & OB_MODE_VERTEX_PAINT)))
99                 ED_object_toggle_modes(C, OB_MODE_VERTEX_PAINT);
100
101         /* reconstruct active layer */
102         if (actlayerdata != layerdata) {
103                 /* find index */
104                 actindex = CustomData_get_layer_index(data, type);
105                 for (i=actindex; i<data->totlayer; i++) {
106                         if (data->layers[i].data == actlayerdata) {
107                                 actindex = i - actindex;
108                                 break;
109                         }
110                 }
111                 
112                 /* set index */
113                 CustomData_set_layer_active(data, type, actindex);
114         }
115         
116         if (rndlayerdata != layerdata) {
117                 /* find index */
118                 rndindex = CustomData_get_layer_index(data, type);
119                 for (i=rndindex; i<data->totlayer; i++) {
120                         if (data->layers[i].data == rndlayerdata) {
121                                 rndindex = i - rndindex;
122                                 break;
123                         }
124                 }
125                 
126                 /* set index */
127                 CustomData_set_layer_render(data, type, rndindex);
128         }
129         
130         if (clonelayerdata != layerdata) {
131                 /* find index */
132                 cloneindex = CustomData_get_layer_index(data, type);
133                 for (i=cloneindex; i<data->totlayer; i++) {
134                         if (data->layers[i].data == clonelayerdata) {
135                                 cloneindex = i - cloneindex;
136                                 break;
137                         }
138                 }
139                 
140                 /* set index */
141                 CustomData_set_layer_clone(data, type, cloneindex);
142         }
143         
144         if (masklayerdata != layerdata) {
145                 /* find index */
146                 maskindex = CustomData_get_layer_index(data, type);
147                 for (i=maskindex; i<data->totlayer; i++) {
148                         if (data->layers[i].data == masklayerdata) {
149                                 maskindex = i - maskindex;
150                                 break;
151                         }
152                 }
153                 
154                 /* set index */
155                 CustomData_set_layer_mask(data, type, maskindex);
156         }
157 }
158
159 int ED_mesh_uv_texture_add(bContext *C, Scene *scene, Object *ob, Mesh *me)
160 {
161         EditMesh *em;
162         int layernum;
163
164         if(me->edit_mesh) {
165                 em= me->edit_mesh;
166
167                 layernum= CustomData_number_of_layers(&em->fdata, CD_MTFACE);
168                 if(layernum >= MAX_MTFACE)
169                         return OPERATOR_CANCELLED;
170
171                 EM_add_data_layer(em, &em->fdata, CD_MTFACE);
172                 CustomData_set_layer_active(&em->fdata, CD_MTFACE, layernum);
173         }
174         else {
175                 layernum= CustomData_number_of_layers(&me->fdata, CD_MTFACE);
176                 if(layernum >= MAX_MTFACE)
177                         return OPERATOR_CANCELLED;
178
179                 if(me->mtface)
180                         CustomData_add_layer(&me->fdata, CD_MTFACE, CD_DUPLICATE, me->mtface, me->totface);
181                 else
182                         CustomData_add_layer(&me->fdata, CD_MTFACE, CD_DEFAULT, NULL, me->totface);
183
184                 CustomData_set_layer_active(&me->fdata, CD_MTFACE, layernum);
185                 mesh_update_customdata_pointers(me);
186         }
187
188         DAG_id_flush_update(&me->id, OB_RECALC_DATA);
189         WM_event_add_notifier(C, NC_GEOM|ND_DATA, me);
190
191         return 1;
192 }
193
194 int ED_mesh_uv_texture_remove(bContext *C, Object *ob, Mesh *me)
195 {
196         CustomDataLayer *cdl;
197         int index;
198
199         index= CustomData_get_active_layer_index(&me->fdata, CD_MTFACE);
200         cdl= (index == -1)? NULL: &me->fdata.layers[index];
201
202         if(!cdl)
203                 return 0;
204
205         delete_customdata_layer(C, ob, cdl);
206         DAG_id_flush_update(&me->id, OB_RECALC_DATA);
207         WM_event_add_notifier(C, NC_GEOM|ND_DATA, me);
208
209         return 1;
210 }
211
212 int ED_mesh_color_add(bContext *C, Scene *scene, Object *ob, Mesh *me)
213 {
214         EditMesh *em;
215         MCol *mcol;
216         int layernum;
217
218         if(me->edit_mesh) {
219                 em= me->edit_mesh;
220
221                 layernum= CustomData_number_of_layers(&em->fdata, CD_MCOL);
222                 if(layernum >= MAX_MCOL)
223                         return 0;
224
225                 EM_add_data_layer(em, &em->fdata, CD_MCOL);
226                 CustomData_set_layer_active(&em->fdata, CD_MCOL, layernum);
227         }
228         else {
229                 layernum= CustomData_number_of_layers(&me->fdata, CD_MCOL);
230                 if(layernum >= MAX_MCOL)
231                         return 0;
232
233                 mcol= me->mcol;
234
235                 if(me->mcol)
236                         CustomData_add_layer(&me->fdata, CD_MCOL, CD_DUPLICATE, me->mcol, me->totface);
237                 else
238                         CustomData_add_layer(&me->fdata, CD_MCOL, CD_DEFAULT, NULL, me->totface);
239
240                 CustomData_set_layer_active(&me->fdata, CD_MCOL, layernum);
241                 mesh_update_customdata_pointers(me);
242
243                 if(!mcol)
244                         shadeMeshMCol(scene, ob, me);
245         }
246
247         DAG_id_flush_update(&me->id, OB_RECALC_DATA);
248         WM_event_add_notifier(C, NC_GEOM|ND_DATA, me);
249
250         return 1;
251 }
252
253 int ED_mesh_color_remove(bContext *C, Object *ob, Mesh *me)
254 {
255         CustomDataLayer *cdl;
256         int index;
257
258         index= CustomData_get_active_layer_index(&me->fdata, CD_MCOL);
259         cdl= (index == -1)? NULL: &me->fdata.layers[index];
260
261         if(!cdl)
262                 return 0;
263
264         delete_customdata_layer(C, ob, cdl);
265         DAG_id_flush_update(&me->id, OB_RECALC_DATA);
266         WM_event_add_notifier(C, NC_GEOM|ND_DATA, me);
267
268         return 1;
269 }
270
271 /*********************** UV texture operators ************************/
272
273 static int layers_poll(bContext *C)
274 {
275         Object *ob= CTX_data_pointer_get_type(C, "object", &RNA_Object).data;
276         ID *data= (ob)? ob->data: NULL;
277         return (ob && !ob->id.lib && ob->type==OB_MESH && data && !data->lib);
278 }
279
280 static int uv_texture_add_exec(bContext *C, wmOperator *op)
281 {
282         Scene *scene= CTX_data_scene(C);
283         Object *ob= CTX_data_pointer_get_type(C, "object", &RNA_Object).data;
284         Mesh *me= ob->data;
285
286         if(!ED_mesh_uv_texture_add(C, scene, ob, me))
287                 return OPERATOR_CANCELLED;
288
289         return OPERATOR_FINISHED;
290 }
291
292 void MESH_OT_uv_texture_add(wmOperatorType *ot)
293 {
294         /* identifiers */
295         ot->name= "Add UV Texture";
296         ot->description= "Add UV texture layer.";
297         ot->idname= "MESH_OT_uv_texture_add";
298         
299         /* api callbacks */
300         ot->poll= layers_poll;
301         ot->exec= uv_texture_add_exec;
302
303         /* flags */
304         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
305 }
306
307 static int uv_texture_remove_exec(bContext *C, wmOperator *op)
308 {
309         Object *ob= CTX_data_pointer_get_type(C, "object", &RNA_Object).data;
310         Mesh *me= ob->data;
311
312         if(!ED_mesh_uv_texture_remove(C, ob, me))
313                 return OPERATOR_CANCELLED;
314
315         return OPERATOR_FINISHED;
316 }
317
318 void MESH_OT_uv_texture_remove(wmOperatorType *ot)
319 {
320         /* identifiers */
321         ot->name= "Remove UV Texture";
322         ot->description= "Remove UV texture layer.";
323         ot->idname= "MESH_OT_uv_texture_remove";
324         
325         /* api callbacks */
326         ot->poll= layers_poll;
327         ot->exec= uv_texture_remove_exec;
328
329         /* flags */
330         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
331 }
332
333 /*********************** vertex color operators ************************/
334
335 static int vertex_color_add_exec(bContext *C, wmOperator *op)
336 {
337         Scene *scene= CTX_data_scene(C);
338         Object *ob= CTX_data_pointer_get_type(C, "object", &RNA_Object).data;
339         Mesh *me= ob->data;
340
341         if(!ED_mesh_color_add(C, scene, ob, me))
342                 return OPERATOR_CANCELLED;
343
344         return OPERATOR_FINISHED;
345 }
346
347 void MESH_OT_vertex_color_add(wmOperatorType *ot)
348 {
349         /* identifiers */
350         ot->name= "Add Vertex Color";
351         ot->description= "Add vertex color layer.";
352         ot->idname= "MESH_OT_vertex_color_add";
353         
354         /* api callbacks */
355         ot->poll= layers_poll;
356         ot->exec= vertex_color_add_exec;
357
358         /* flags */
359         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
360 }
361
362 static int vertex_color_remove_exec(bContext *C, wmOperator *op)
363 {
364         Object *ob= CTX_data_pointer_get_type(C, "object", &RNA_Object).data;
365         Mesh *me= ob->data;
366
367         if(!ED_mesh_color_remove(C, ob, me))
368                 return OPERATOR_CANCELLED;
369
370         return OPERATOR_FINISHED;
371 }
372
373 void MESH_OT_vertex_color_remove(wmOperatorType *ot)
374 {
375         /* identifiers */
376         ot->name= "Remove Vertex Color";
377         ot->description= "Remove vertex color layer.";
378         ot->idname= "MESH_OT_vertex_color_remove";
379         
380         /* api callbacks */
381         ot->exec= vertex_color_remove_exec;
382         ot->poll= layers_poll;
383
384         /* flags */
385         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
386 }
387
388 /*********************** sticky operators ************************/
389
390 static int sticky_add_exec(bContext *C, wmOperator *op)
391 {
392         Object *ob= CTX_data_pointer_get_type(C, "object", &RNA_Object).data;
393         Mesh *me= ob->data;
394
395         if(me->msticky)
396                 return OPERATOR_CANCELLED;
397
398         // XXX RE_make_sticky();
399
400         DAG_id_flush_update(&me->id, OB_RECALC_DATA);
401         WM_event_add_notifier(C, NC_GEOM|ND_DATA, me);
402
403         return OPERATOR_FINISHED;
404 }
405
406 void MESH_OT_sticky_add(wmOperatorType *ot)
407 {
408         /* identifiers */
409         ot->name= "Add Sticky";
410         ot->description= "Add sticky UV texture layer.";
411         ot->idname= "MESH_OT_sticky_add";
412         
413         /* api callbacks */
414         ot->poll= layers_poll;
415         ot->exec= sticky_add_exec;
416
417         /* flags */
418         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
419 }
420
421 static int sticky_remove_exec(bContext *C, wmOperator *op)
422 {
423         Object *ob= CTX_data_pointer_get_type(C, "object", &RNA_Object).data;
424         Mesh *me= ob->data;
425
426         if(!me->msticky)
427                 return OPERATOR_CANCELLED;
428
429         CustomData_free_layer_active(&me->vdata, CD_MSTICKY, me->totvert);
430         me->msticky= NULL;
431
432         DAG_id_flush_update(&me->id, OB_RECALC_DATA);
433         WM_event_add_notifier(C, NC_GEOM|ND_DATA, me);
434
435         return OPERATOR_FINISHED;
436 }
437
438 void MESH_OT_sticky_remove(wmOperatorType *ot)
439 {
440         /* identifiers */
441         ot->name= "Remove Sticky";
442         ot->description= "Remove sticky UV texture layer.";
443         ot->idname= "MESH_OT_sticky_remove";
444         
445         /* api callbacks */
446         ot->poll= layers_poll;
447         ot->exec= sticky_remove_exec;
448
449         /* flags */
450         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
451 }
452
453 /************************** Add Geometry Layers *************************/
454
455 static void mesh_calc_edges(Mesh *mesh, int update)
456 {
457         CustomData edata;
458         EdgeHashIterator *ehi;
459         MFace *mf = mesh->mface;
460         MEdge *med, *med_orig;
461         EdgeHash *eh = BLI_edgehash_new();
462         int i, *index, totedge, totface = mesh->totface;
463
464         if(mesh->totedge==0)
465                 update= 0;
466
467         if(update) {
468                 /* assume existing edges are valid
469                  * useful when adding more faces and generating edges from them */
470                 med= mesh->medge;
471                 for(i= 0; i<mesh->totedge; i++, med++)
472                         BLI_edgehash_insert(eh, med->v1, med->v2, med);
473         }
474
475         for (i = 0; i < totface; i++, mf++) {
476                 if (!BLI_edgehash_haskey(eh, mf->v1, mf->v2))
477                         BLI_edgehash_insert(eh, mf->v1, mf->v2, NULL);
478                 if (!BLI_edgehash_haskey(eh, mf->v2, mf->v3))
479                         BLI_edgehash_insert(eh, mf->v2, mf->v3, NULL);
480                 
481                 if (mf->v4) {
482                         if (!BLI_edgehash_haskey(eh, mf->v3, mf->v4))
483                                 BLI_edgehash_insert(eh, mf->v3, mf->v4, NULL);
484                         if (!BLI_edgehash_haskey(eh, mf->v4, mf->v1))
485                                 BLI_edgehash_insert(eh, mf->v4, mf->v1, NULL);
486                 } else {
487                         if (!BLI_edgehash_haskey(eh, mf->v3, mf->v1))
488                                 BLI_edgehash_insert(eh, mf->v3, mf->v1, NULL);
489                 }
490         }
491
492         totedge = BLI_edgehash_size(eh);
493
494         /* write new edges into a temporary CustomData */
495         memset(&edata, 0, sizeof(edata));
496         CustomData_add_layer(&edata, CD_MEDGE, CD_CALLOC, NULL, totedge);
497
498         ehi = BLI_edgehashIterator_new(eh);
499         med = CustomData_get_layer(&edata, CD_MEDGE);
500         for(i = 0; !BLI_edgehashIterator_isDone(ehi);
501             BLI_edgehashIterator_step(ehi), ++i, ++med, ++index) {
502
503                 if(update && (med_orig=BLI_edgehashIterator_getValue(ehi))) {
504                         *med= *med_orig; /* copy from the original */
505                 } else {
506                         BLI_edgehashIterator_getKey(ehi, (int*)&med->v1, (int*)&med->v2);
507                         med->flag = ME_EDGEDRAW|ME_EDGERENDER;
508                 }
509         }
510         BLI_edgehashIterator_free(ehi);
511
512         /* free old CustomData and assign new one */
513         CustomData_free(&mesh->edata, mesh->totedge);
514         mesh->edata = edata;
515         mesh->totedge = totedge;
516
517         mesh->medge = CustomData_get_layer(&mesh->edata, CD_MEDGE);
518
519         BLI_edgehash_free(eh, NULL);
520 }
521
522 void ED_mesh_update(Mesh *mesh, bContext *C, int calc_edges)
523 {
524         if(calc_edges || (mesh->totface && mesh->totedge == 0))
525                 mesh_calc_edges(mesh, calc_edges);
526
527         mesh_calc_normals(mesh->mvert, mesh->totvert, mesh->mface, mesh->totface, NULL);
528
529         DAG_id_flush_update(&mesh->id, OB_RECALC_DATA);
530         WM_event_add_notifier(C, NC_GEOM|ND_DATA, mesh);
531 }
532
533 static void mesh_add_verts(Mesh *mesh, int len)
534 {
535         CustomData vdata;
536         MVert *mvert;
537         int i, totvert;
538
539         if(len == 0)
540                 return;
541
542         totvert= mesh->totvert + len;
543         CustomData_copy(&mesh->vdata, &vdata, CD_MASK_MESH, CD_DEFAULT, totvert);
544         CustomData_copy_data(&mesh->vdata, &vdata, 0, 0, mesh->totvert);
545
546         if(!CustomData_has_layer(&vdata, CD_MVERT))
547                 CustomData_add_layer(&vdata, CD_MVERT, CD_CALLOC, NULL, totvert);
548
549         CustomData_free(&mesh->vdata, mesh->totvert);
550         mesh->vdata= vdata;
551         mesh_update_customdata_pointers(mesh);
552
553         /* scan the input list and insert the new vertices */
554
555         mvert= &mesh->mvert[mesh->totvert];
556         for(i=0; i<len; i++, mvert++)
557                 mvert->flag |= SELECT;
558
559         /* set final vertex list size */
560         mesh->totvert= totvert;
561 }
562
563 void ED_mesh_transform(Mesh *me, float *mat)
564 {
565         int i;
566         MVert *mvert= me->mvert;
567
568         for(i= 0; i < me->totvert; i++, mvert++)
569                 Mat4MulVecfl((float (*)[4])mat, mvert->co);
570
571         mesh_calc_normals(me->mvert, me->totvert, me->mface, me->totface, NULL);
572 }
573
574 static void mesh_add_edges(Mesh *mesh, int len)
575 {
576         CustomData edata;
577         MEdge *medge;
578         int i, totedge;
579
580         if(len == 0)
581                 return;
582
583         totedge= mesh->totedge+len;
584
585         /* update customdata  */
586         CustomData_copy(&mesh->edata, &edata, CD_MASK_MESH, CD_DEFAULT, totedge);
587         CustomData_copy_data(&mesh->edata, &edata, 0, 0, mesh->totedge);
588
589         if(!CustomData_has_layer(&edata, CD_MEDGE))
590                 CustomData_add_layer(&edata, CD_MEDGE, CD_CALLOC, NULL, totedge);
591
592         CustomData_free(&mesh->edata, mesh->totedge);
593         mesh->edata= edata;
594         mesh_update_customdata_pointers(mesh);
595
596         /* set default flags */
597         medge= &mesh->medge[mesh->totedge];
598         for(i=0; i<len; i++, medge++)
599                 medge->flag= ME_EDGEDRAW|ME_EDGERENDER|SELECT;
600
601         mesh->totedge= totedge;
602 }
603
604 static void mesh_add_faces(Mesh *mesh, int len)
605 {
606         CustomData fdata;
607         MFace *mface;
608         int i, totface;
609
610         if(len == 0)
611                 return;
612
613         totface= mesh->totface + len;   /* new face count */
614
615         /* update customdata */
616         CustomData_copy(&mesh->fdata, &fdata, CD_MASK_MESH, CD_DEFAULT, totface);
617         CustomData_copy_data(&mesh->fdata, &fdata, 0, 0, mesh->totface);
618
619         if(!CustomData_has_layer(&fdata, CD_MFACE))
620                 CustomData_add_layer(&fdata, CD_MFACE, CD_CALLOC, NULL, totface);
621
622         CustomData_free(&mesh->fdata, mesh->totface);
623         mesh->fdata= fdata;
624         mesh_update_customdata_pointers(mesh);
625
626         /* set default flags */
627         mface= &mesh->mface[mesh->totface];
628         for(i=0; i<len; i++, mface++)
629                 mface->flag= SELECT;
630
631         mesh->totface= totface;
632 }
633
634 void ED_mesh_geometry_add(Mesh *mesh, ReportList *reports, int verts, int edges, int faces)
635 {
636         if(mesh->edit_mesh) {
637                 BKE_report(reports, RPT_ERROR, "Can't add geometry in edit mode.");
638                 return;
639         }
640
641         if(verts)
642                 mesh_add_verts(mesh, verts);
643         if(edges)
644                 mesh_add_edges(mesh, edges);
645         if(faces)
646                 mesh_add_faces(mesh, faces);
647 }
648
649 void ED_mesh_calc_normals(Mesh *me)
650 {
651         mesh_calc_normals(me->mvert, me->totvert, me->mface, me->totface, NULL);
652 }
653
654 void ED_mesh_material_add(Mesh *me, Material *ma)
655 {
656         int i;
657         int totcol = me->totcol + 1;
658         Material **mat;
659
660         /* don't add if mesh already has it */
661         for(i = 0; i < me->totcol; i++)
662                 if(me->mat[i] == ma)
663                         return;
664
665         mat= MEM_callocN(sizeof(void*)*totcol, "newmatar");
666
667         if(me->totcol) memcpy(mat, me->mat, sizeof(void*) * me->totcol);
668         if(me->mat) MEM_freeN(me->mat);
669
670         me->mat = mat;
671         me->mat[me->totcol++] = ma;
672         ma->id.us++;
673
674         test_object_materials((ID*)me);
675 }
676