2008-12-30 13:16:14 +00:00
|
|
|
/**
|
|
|
|
* $Id:
|
|
|
|
*
|
|
|
|
* ***** BEGIN GPL LICENSE BLOCK *****
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
* as published by the Free Software Foundation; either version 2
|
|
|
|
* of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software Foundation,
|
2010-02-12 13:34:04 +00:00
|
|
|
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
2008-12-30 13:16:14 +00:00
|
|
|
*
|
|
|
|
* The Original Code is Copyright (C) 2004 by Blender Foundation
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* The Original Code is: all of this file.
|
|
|
|
*
|
|
|
|
* Contributor(s): none yet.
|
|
|
|
*
|
|
|
|
* ***** END GPL LICENSE BLOCK *****
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
meshtools.c: no editmode (violated already :), tools operating on meshes
|
|
|
|
*/
|
|
|
|
|
2009-11-28 04:04:01 +00:00
|
|
|
#include <stddef.h>
|
2008-12-30 13:16:14 +00:00
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <math.h>
|
2009-09-06 13:20:05 +00:00
|
|
|
#include <float.h>
|
2008-12-30 13:16:14 +00:00
|
|
|
|
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
|
|
|
|
#include "DNA_image_types.h"
|
2009-07-13 00:40:20 +00:00
|
|
|
#include "DNA_key_types.h"
|
|
|
|
#include "DNA_material_types.h"
|
2008-12-30 13:16:14 +00:00
|
|
|
#include "DNA_meshdata_types.h"
|
2009-07-13 00:40:20 +00:00
|
|
|
#include "DNA_mesh_types.h"
|
2008-12-30 13:16:14 +00:00
|
|
|
#include "DNA_object_types.h"
|
|
|
|
#include "DNA_scene_types.h"
|
|
|
|
#include "DNA_screen_types.h"
|
|
|
|
#include "DNA_space_types.h"
|
|
|
|
#include "DNA_view3d_types.h"
|
2009-02-14 21:31:34 +00:00
|
|
|
#include "DNA_windowmanager_types.h"
|
2009-07-13 00:40:20 +00:00
|
|
|
#include "DNA_world_types.h"
|
2008-12-30 13:16:14 +00:00
|
|
|
|
2009-11-10 20:43:45 +00:00
|
|
|
#include "BLI_math.h"
|
2008-12-30 13:16:14 +00:00
|
|
|
#include "BLI_blenlib.h"
|
|
|
|
#include "BLI_editVert.h"
|
|
|
|
#include "BLI_ghash.h"
|
|
|
|
#include "BLI_rand.h" /* for randome face sorting */
|
|
|
|
#include "BLI_threads.h"
|
|
|
|
|
|
|
|
|
|
|
|
#include "BKE_blender.h"
|
2009-07-13 00:40:20 +00:00
|
|
|
#include "BKE_context.h"
|
2008-12-30 13:16:14 +00:00
|
|
|
#include "BKE_depsgraph.h"
|
2009-11-28 04:04:01 +00:00
|
|
|
#include "BKE_DerivedMesh.h"
|
2008-12-30 13:16:14 +00:00
|
|
|
#include "BKE_customdata.h"
|
|
|
|
#include "BKE_global.h"
|
|
|
|
#include "BKE_image.h"
|
2009-07-13 00:40:20 +00:00
|
|
|
#include "BKE_key.h"
|
2008-12-30 13:16:14 +00:00
|
|
|
#include "BKE_library.h"
|
|
|
|
#include "BKE_main.h"
|
|
|
|
#include "BKE_mesh.h"
|
|
|
|
#include "BKE_material.h"
|
|
|
|
#include "BKE_object.h"
|
|
|
|
#include "BKE_utildefines.h"
|
2009-02-14 21:31:34 +00:00
|
|
|
#include "BKE_report.h"
|
2008-12-30 13:16:14 +00:00
|
|
|
|
|
|
|
#include "BLO_sys_types.h" // for intptr_t support
|
|
|
|
|
|
|
|
#include "ED_mesh.h"
|
|
|
|
#include "ED_object.h"
|
2009-01-05 15:19:31 +00:00
|
|
|
#include "ED_view3d.h"
|
2008-12-30 13:16:14 +00:00
|
|
|
|
2009-07-13 00:40:20 +00:00
|
|
|
#include "WM_api.h"
|
|
|
|
#include "WM_types.h"
|
|
|
|
|
2008-12-30 13:16:14 +00:00
|
|
|
/* own include */
|
2009-01-01 13:15:35 +00:00
|
|
|
#include "mesh_intern.h"
|
2008-12-30 13:16:14 +00:00
|
|
|
|
|
|
|
/* XXX */
|
|
|
|
static int pupmenu() {return 0;}
|
|
|
|
/* XXX */
|
|
|
|
|
|
|
|
|
|
|
|
/* * ********************** no editmode!!! *********** */
|
|
|
|
|
2009-07-13 00:40:20 +00:00
|
|
|
/*********************** JOIN ***************************/
|
|
|
|
|
2008-12-30 13:16:14 +00:00
|
|
|
/* join selected meshes into the active mesh, context sensitive
|
|
|
|
return 0 if no join is made (error) and 1 of the join is done */
|
2009-07-11 10:20:48 +00:00
|
|
|
|
2009-07-13 00:40:20 +00:00
|
|
|
int join_mesh_exec(bContext *C, wmOperator *op)
|
2008-12-30 13:16:14 +00:00
|
|
|
{
|
2009-07-13 00:40:20 +00:00
|
|
|
Scene *scene= CTX_data_scene(C);
|
|
|
|
Object *ob= CTX_data_active_object(C);
|
2008-12-30 13:16:14 +00:00
|
|
|
Material **matar, *ma;
|
|
|
|
Mesh *me;
|
2009-07-13 00:40:20 +00:00
|
|
|
MVert *mvert, *mv, *mvertmain;
|
2008-12-30 13:16:14 +00:00
|
|
|
MEdge *medge = NULL, *medgemain;
|
|
|
|
MFace *mface = NULL, *mfacemain;
|
2009-07-13 00:40:20 +00:00
|
|
|
Key *key, *nkey=NULL;
|
|
|
|
KeyBlock *kb, *okb, *kbn;
|
|
|
|
float imat[4][4], cmat[4][4], *fp1, *fp2, curpos;
|
|
|
|
int a, b, totcol, totmat=0, totedge=0, totvert=0, totface=0, ok=0;
|
2009-11-10 06:29:10 +00:00
|
|
|
int vertofs, *matmap=NULL;
|
2009-07-13 00:40:20 +00:00
|
|
|
int i, j, index, haskey=0, edgeofs, faceofs;
|
2008-12-30 13:16:14 +00:00
|
|
|
bDeformGroup *dg, *odg;
|
|
|
|
MDeformVert *dvert;
|
|
|
|
CustomData vdata, edata, fdata;
|
|
|
|
|
2009-07-13 00:40:20 +00:00
|
|
|
if(scene->obedit)
|
|
|
|
return OPERATOR_CANCELLED;
|
2008-12-30 13:16:14 +00:00
|
|
|
|
2009-07-13 00:40:20 +00:00
|
|
|
/* ob is the object we are adding geometry to */
|
|
|
|
if(!ob || ob->type!=OB_MESH)
|
|
|
|
return OPERATOR_CANCELLED;
|
2008-12-30 13:16:14 +00:00
|
|
|
|
|
|
|
/* count & check */
|
2009-07-13 00:40:20 +00:00
|
|
|
CTX_DATA_BEGIN(C, Base*, base, selected_editable_bases) {
|
|
|
|
if(base->object->type==OB_MESH) {
|
|
|
|
me= base->object->data;
|
|
|
|
|
|
|
|
totvert+= me->totvert;
|
|
|
|
totedge+= me->totedge;
|
|
|
|
totface+= me->totface;
|
|
|
|
totmat+= base->object->totcol;
|
|
|
|
|
|
|
|
if(base->object == ob)
|
|
|
|
ok= 1;
|
|
|
|
|
|
|
|
/* check for shapekeys */
|
|
|
|
if(me->key)
|
|
|
|
haskey++;
|
2008-12-30 13:16:14 +00:00
|
|
|
}
|
|
|
|
}
|
2009-07-13 00:40:20 +00:00
|
|
|
CTX_DATA_END;
|
2008-12-30 13:16:14 +00:00
|
|
|
|
|
|
|
/* that way the active object is always selected */
|
2009-07-13 00:40:20 +00:00
|
|
|
if(ok==0)
|
|
|
|
return OPERATOR_CANCELLED;
|
2008-12-30 13:16:14 +00:00
|
|
|
|
2009-07-13 00:40:20 +00:00
|
|
|
/* only join meshes if there are verts to join, there aren't too many, and we only had one mesh selected */
|
|
|
|
me= (Mesh *)ob->data;
|
|
|
|
key= me->key;
|
|
|
|
if(totvert==0 || totvert>MESH_MAX_VERTS || totvert==me->totvert)
|
|
|
|
return OPERATOR_CANCELLED;
|
2008-12-30 13:16:14 +00:00
|
|
|
|
|
|
|
/* new material indices and material array */
|
2009-07-13 00:40:20 +00:00
|
|
|
matar= MEM_callocN(sizeof(void*)*totmat, "join_mesh matar");
|
2009-11-10 06:29:10 +00:00
|
|
|
if (totmat) matmap= MEM_callocN(sizeof(int)*totmat, "join_mesh matmap");
|
2008-12-30 13:16:14 +00:00
|
|
|
totcol= ob->totcol;
|
|
|
|
|
|
|
|
/* obact materials in new main array, is nicer start! */
|
2009-07-13 00:40:20 +00:00
|
|
|
for(a=0; a<ob->totcol; a++) {
|
|
|
|
matar[a]= give_current_material(ob, a+1);
|
|
|
|
id_us_plus((ID *)matar[a]);
|
2008-12-30 13:16:14 +00:00
|
|
|
/* increase id->us : will be lowered later */
|
|
|
|
}
|
|
|
|
|
2009-07-13 00:40:20 +00:00
|
|
|
/* - if destination mesh had shapekeys, move them somewhere safe, and set up placeholders
|
|
|
|
* with arrays that are large enough to hold shapekey data for all meshes
|
|
|
|
* - if destination mesh didn't have shapekeys, but we encountered some in the meshes we're
|
|
|
|
* joining, set up a new keyblock and assign to the mesh
|
|
|
|
*/
|
|
|
|
if(key) {
|
|
|
|
/* make a duplicate copy that will only be used here... (must remember to free it!) */
|
|
|
|
nkey= copy_key(key);
|
|
|
|
|
|
|
|
/* for all keys in old block, clear data-arrays */
|
|
|
|
for(kb= key->block.first; kb; kb= kb->next) {
|
|
|
|
if(kb->data) MEM_freeN(kb->data);
|
|
|
|
kb->data= MEM_callocN(sizeof(float)*3*totvert, "join_shapekey");
|
|
|
|
kb->totelem= totvert;
|
|
|
|
kb->weights= NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if(haskey) {
|
|
|
|
/* add a new key-block and add to the mesh */
|
|
|
|
key= me->key= add_key((ID *)me);
|
|
|
|
key->type = KEY_RELATIVE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* first pass over objects - copying materials and vertexgroups across */
|
|
|
|
CTX_DATA_BEGIN(C, Base*, base, selected_editable_bases) {
|
|
|
|
/* only act if a mesh, and not the one we're joining to */
|
|
|
|
if((ob!=base->object) && (base->object->type==OB_MESH)) {
|
|
|
|
me= base->object->data;
|
|
|
|
|
|
|
|
/* Join this object's vertex groups to the base one's */
|
|
|
|
for(dg=base->object->defbase.first; dg; dg=dg->next) {
|
|
|
|
/* See if this group exists in the object (if it doesn't, add it to the end) */
|
|
|
|
for(odg=ob->defbase.first; odg; odg=odg->next) {
|
|
|
|
if(!strcmp(odg->name, dg->name)) {
|
|
|
|
break;
|
2008-12-30 13:16:14 +00:00
|
|
|
}
|
|
|
|
}
|
2009-07-13 00:40:20 +00:00
|
|
|
if(!odg) {
|
|
|
|
odg = MEM_callocN(sizeof(bDeformGroup), "join deformGroup");
|
|
|
|
memcpy(odg, dg, sizeof(bDeformGroup));
|
|
|
|
BLI_addtail(&ob->defbase, odg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(ob->defbase.first && ob->actdef==0)
|
|
|
|
ob->actdef=1;
|
|
|
|
|
|
|
|
|
|
|
|
if(me->totvert) {
|
|
|
|
/* Add this object's materials to the base one's if they don't exist already (but only if limits not exceeded yet) */
|
|
|
|
if(totcol < MAXMAT-1) {
|
2008-12-30 13:16:14 +00:00
|
|
|
for(a=1; a<=base->object->totcol; a++) {
|
|
|
|
ma= give_current_material(base->object, a);
|
2009-07-13 00:40:20 +00:00
|
|
|
|
|
|
|
for(b=0; b<totcol; b++) {
|
|
|
|
if(ma == matar[b]) break;
|
|
|
|
}
|
|
|
|
if(b==totcol) {
|
|
|
|
matar[b]= ma;
|
|
|
|
if(ma)
|
2008-12-30 13:16:14 +00:00
|
|
|
ma->id.us++;
|
2009-07-13 00:40:20 +00:00
|
|
|
totcol++;
|
|
|
|
}
|
|
|
|
if(totcol>=MAXMAT-1)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* if this mesh has shapekeys, check if destination mesh already has matching entries too */
|
|
|
|
if(me->key && key) {
|
|
|
|
for(kb= me->key->block.first; kb; kb= kb->next) {
|
|
|
|
/* if key doesn't exist in destination mesh, add it */
|
|
|
|
if(key_get_named_keyblock(key, kb->name) == NULL) {
|
|
|
|
/* copy this existing one over to the new shapekey block */
|
|
|
|
kbn= MEM_dupallocN(kb);
|
|
|
|
kbn->prev= kbn->next= NULL;
|
|
|
|
|
|
|
|
/* adjust adrcode and other settings to fit (allocate a new data-array) */
|
|
|
|
kbn->data= MEM_callocN(sizeof(float)*3*totvert, "joined_shapekey");
|
|
|
|
kbn->totelem= totvert;
|
|
|
|
kbn->weights= NULL;
|
|
|
|
|
|
|
|
okb= key->block.last;
|
|
|
|
curpos= (okb) ? okb->pos : -0.1f;
|
|
|
|
if(key->type == KEY_RELATIVE)
|
|
|
|
kbn->pos= curpos + 0.1f;
|
|
|
|
else
|
|
|
|
kbn->pos= curpos;
|
|
|
|
|
|
|
|
BLI_addtail(&key->block, kbn);
|
|
|
|
kbn->adrcode= key->totkey;
|
|
|
|
key->totkey++;
|
|
|
|
if(key->totkey==1) key->refkey= kbn;
|
|
|
|
|
|
|
|
// XXX 2.5 Animato
|
|
|
|
#if 0
|
|
|
|
/* also, copy corresponding ipo-curve to ipo-block if applicable */
|
|
|
|
if(me->key->ipo && key->ipo) {
|
|
|
|
// FIXME... this is a luxury item!
|
|
|
|
puts("FIXME: ignoring IPO's when joining shapekeys on Meshes for now...");
|
2008-12-30 13:16:14 +00:00
|
|
|
}
|
2009-07-13 00:40:20 +00:00
|
|
|
#endif
|
2008-12-30 13:16:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2009-07-13 00:40:20 +00:00
|
|
|
CTX_DATA_END;
|
|
|
|
|
|
|
|
/* setup new data for destination mesh */
|
2008-12-30 13:16:14 +00:00
|
|
|
memset(&vdata, 0, sizeof(vdata));
|
|
|
|
memset(&edata, 0, sizeof(edata));
|
|
|
|
memset(&fdata, 0, sizeof(fdata));
|
|
|
|
|
|
|
|
mvert= CustomData_add_layer(&vdata, CD_MVERT, CD_CALLOC, NULL, totvert);
|
|
|
|
medge= CustomData_add_layer(&edata, CD_MEDGE, CD_CALLOC, NULL, totedge);
|
|
|
|
mface= CustomData_add_layer(&fdata, CD_MFACE, CD_CALLOC, NULL, totface);
|
2009-07-13 00:40:20 +00:00
|
|
|
|
2008-12-30 13:16:14 +00:00
|
|
|
mvertmain= mvert;
|
|
|
|
medgemain= medge;
|
|
|
|
mfacemain= mface;
|
2009-07-13 00:40:20 +00:00
|
|
|
|
2008-12-30 13:16:14 +00:00
|
|
|
vertofs= 0;
|
|
|
|
edgeofs= 0;
|
|
|
|
faceofs= 0;
|
2009-07-13 00:40:20 +00:00
|
|
|
|
|
|
|
/* inverse transform for all selected meshes in this object */
|
2009-11-10 20:43:45 +00:00
|
|
|
invert_m4_m4(imat, ob->obmat);
|
2009-07-13 00:40:20 +00:00
|
|
|
|
|
|
|
CTX_DATA_BEGIN(C, Base*, base, selected_editable_bases) {
|
|
|
|
/* only join if this is a mesh */
|
|
|
|
if(base->object->type==OB_MESH) {
|
|
|
|
me= base->object->data;
|
|
|
|
|
|
|
|
if(me->totvert) {
|
|
|
|
/* standard data */
|
|
|
|
CustomData_merge(&me->vdata, &vdata, CD_MASK_MESH, CD_DEFAULT, totvert);
|
|
|
|
CustomData_copy_data(&me->vdata, &vdata, 0, vertofs, me->totvert);
|
2008-12-30 13:16:14 +00:00
|
|
|
|
2009-07-13 00:40:20 +00:00
|
|
|
/* vertex groups */
|
|
|
|
dvert= CustomData_get(&vdata, vertofs, CD_MDEFORMVERT);
|
2008-12-30 13:16:14 +00:00
|
|
|
|
2009-07-13 00:40:20 +00:00
|
|
|
/* NB: vertex groups here are new version */
|
|
|
|
if(dvert) {
|
|
|
|
for(i=0; i<me->totvert; i++) {
|
|
|
|
for(j=0; j<dvert[i].totweight; j++) {
|
|
|
|
/* Find the old vertex group */
|
|
|
|
odg = BLI_findlink(&base->object->defbase, dvert[i].dw[j].def_nr);
|
|
|
|
if(odg) {
|
|
|
|
/* Search for a match in the new object, and set new index */
|
|
|
|
for(dg=ob->defbase.first, index=0; dg; dg=dg->next, index++) {
|
|
|
|
if(!strcmp(dg->name, odg->name)) {
|
|
|
|
dvert[i].dw[j].def_nr = index;
|
|
|
|
break;
|
2008-12-30 13:16:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-07-13 00:40:20 +00:00
|
|
|
/* if this is the object we're merging into, no need to do anything */
|
|
|
|
if(base->object != ob) {
|
|
|
|
/* watch this: switch matmul order really goes wrong */
|
2009-11-10 20:43:45 +00:00
|
|
|
mul_m4_m4m4(cmat, base->object->obmat, imat);
|
2009-07-13 00:40:20 +00:00
|
|
|
|
|
|
|
/* transform vertex coordinates into new space */
|
|
|
|
for(a=0, mv=mvert; a < me->totvert; a++, mv++) {
|
2009-11-10 20:43:45 +00:00
|
|
|
mul_m4_v3(cmat, mv->co);
|
2009-07-13 00:40:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* for each shapekey in destination mesh:
|
|
|
|
* - if there's a matching one, copy it across (will need to transform vertices into new space...)
|
|
|
|
* - otherwise, just copy own coordinates of mesh (no need to transform vertex coordinates into new space)
|
|
|
|
*/
|
|
|
|
if(key) {
|
|
|
|
/* if this mesh has any shapekeys, check first, otherwise just copy coordinates */
|
|
|
|
for(kb= key->block.first; kb; kb= kb->next) {
|
|
|
|
/* get pointer to where to write data for this mesh in shapekey's data array */
|
|
|
|
fp1= ((float *)kb->data) + (vertofs*3);
|
|
|
|
|
|
|
|
/* check if this mesh has such a shapekey */
|
|
|
|
okb= key_get_named_keyblock(me->key, kb->name);
|
|
|
|
if(okb) {
|
|
|
|
/* copy this mesh's shapekey to the destination shapekey (need to transform first) */
|
|
|
|
fp2= ((float *)(okb->data));
|
|
|
|
for(a=0; a < me->totvert; a++, fp1+=3, fp2+=3) {
|
|
|
|
VECCOPY(fp1, fp2);
|
2009-11-10 20:43:45 +00:00
|
|
|
mul_m4_v3(cmat, fp1);
|
2009-07-13 00:40:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* copy this mesh's vertex coordinates to the destination shapekey */
|
|
|
|
mv= mvert;
|
|
|
|
for(a=0; a < me->totvert; a++, fp1+=3, mv++) {
|
|
|
|
VECCOPY(fp1, mv->co);
|
2008-12-30 13:16:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2009-07-13 00:40:20 +00:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* for each shapekey in destination mesh:
|
|
|
|
* - if it was an 'original', copy the appropriate data from nkey
|
|
|
|
* - otherwise, copy across plain coordinates (no need to transform coordinates)
|
|
|
|
*/
|
|
|
|
if(key) {
|
|
|
|
for(kb= key->block.first; kb; kb= kb->next) {
|
|
|
|
/* get pointer to where to write data for this mesh in shapekey's data array */
|
|
|
|
fp1= ((float *)kb->data) + (vertofs*3);
|
|
|
|
|
|
|
|
/* check if this was one of the original shapekeys */
|
|
|
|
okb= key_get_named_keyblock(nkey, kb->name);
|
|
|
|
if(okb) {
|
|
|
|
/* copy this mesh's shapekey to the destination shapekey */
|
|
|
|
fp2= ((float *)(okb->data));
|
|
|
|
for(a=0; a < me->totvert; a++, fp1+=3, fp2+=3) {
|
|
|
|
VECCOPY(fp1, fp2);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* copy base-coordinates to the destination shapekey */
|
|
|
|
mv= mvert;
|
|
|
|
for(a=0; a < me->totvert; a++, fp1+=3, mv++) {
|
|
|
|
VECCOPY(fp1, mv->co);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2008-12-30 13:16:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-07-13 00:40:20 +00:00
|
|
|
/* advance mvert pointer to end of base mesh's data */
|
|
|
|
mvert+= me->totvert;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(me->totface) {
|
|
|
|
/* make mapping for materials */
|
|
|
|
for(a=1; a<=base->object->totcol; a++) {
|
|
|
|
ma= give_current_material(base->object, a);
|
|
|
|
|
|
|
|
for(b=0; b<totcol; b++) {
|
|
|
|
if(ma == matar[b]) {
|
|
|
|
matmap[a-1]= b;
|
|
|
|
break;
|
|
|
|
}
|
2008-12-30 13:16:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-07-13 00:40:20 +00:00
|
|
|
CustomData_merge(&me->fdata, &fdata, CD_MASK_MESH, CD_DEFAULT, totface);
|
|
|
|
CustomData_copy_data(&me->fdata, &fdata, 0, faceofs, me->totface);
|
|
|
|
|
|
|
|
for(a=0; a<me->totface; a++, mface++) {
|
|
|
|
mface->v1+= vertofs;
|
|
|
|
mface->v2+= vertofs;
|
|
|
|
mface->v3+= vertofs;
|
|
|
|
if(mface->v4) mface->v4+= vertofs;
|
|
|
|
|
2009-11-10 06:29:10 +00:00
|
|
|
if (matmap)
|
|
|
|
mface->mat_nr= matmap[(int)mface->mat_nr];
|
|
|
|
else
|
|
|
|
mface->mat_nr= 0;
|
2009-07-13 00:40:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
faceofs += me->totface;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(me->totedge) {
|
|
|
|
CustomData_merge(&me->edata, &edata, CD_MASK_MESH, CD_DEFAULT, totedge);
|
|
|
|
CustomData_copy_data(&me->edata, &edata, 0, edgeofs, me->totedge);
|
|
|
|
|
|
|
|
for(a=0; a<me->totedge; a++, medge++) {
|
|
|
|
medge->v1+= vertofs;
|
|
|
|
medge->v2+= vertofs;
|
|
|
|
}
|
2008-12-30 13:16:14 +00:00
|
|
|
|
2009-07-13 00:40:20 +00:00
|
|
|
edgeofs += me->totedge;
|
2008-12-30 13:16:14 +00:00
|
|
|
}
|
2009-07-13 00:40:20 +00:00
|
|
|
|
|
|
|
/* vertofs is used to help newly added verts be reattached to their edge/face
|
|
|
|
* (cannot be set earlier, or else reattaching goes wrong)
|
|
|
|
*/
|
|
|
|
vertofs += me->totvert;
|
|
|
|
|
|
|
|
/* free base, now that data is merged */
|
|
|
|
if(base->object != ob)
|
|
|
|
ED_base_object_free_and_unlink(scene, base);
|
2008-12-30 13:16:14 +00:00
|
|
|
}
|
|
|
|
}
|
2009-07-13 00:40:20 +00:00
|
|
|
CTX_DATA_END;
|
2008-12-30 13:16:14 +00:00
|
|
|
|
2009-07-13 00:40:20 +00:00
|
|
|
/* return to mesh we're merging to */
|
2008-12-30 13:16:14 +00:00
|
|
|
me= ob->data;
|
|
|
|
|
|
|
|
CustomData_free(&me->vdata, me->totvert);
|
|
|
|
CustomData_free(&me->edata, me->totedge);
|
|
|
|
CustomData_free(&me->fdata, me->totface);
|
|
|
|
|
|
|
|
me->totvert= totvert;
|
|
|
|
me->totedge= totedge;
|
|
|
|
me->totface= totface;
|
|
|
|
|
|
|
|
me->vdata= vdata;
|
|
|
|
me->edata= edata;
|
|
|
|
me->fdata= fdata;
|
|
|
|
|
|
|
|
mesh_update_customdata_pointers(me);
|
|
|
|
|
|
|
|
/* old material array */
|
|
|
|
for(a=1; a<=ob->totcol; a++) {
|
|
|
|
ma= ob->mat[a-1];
|
|
|
|
if(ma) ma->id.us--;
|
|
|
|
}
|
|
|
|
for(a=1; a<=me->totcol; a++) {
|
|
|
|
ma= me->mat[a-1];
|
|
|
|
if(ma) ma->id.us--;
|
|
|
|
}
|
|
|
|
if(ob->mat) MEM_freeN(ob->mat);
|
2009-07-13 00:40:20 +00:00
|
|
|
if(ob->matbits) MEM_freeN(ob->matbits);
|
2008-12-30 13:16:14 +00:00
|
|
|
if(me->mat) MEM_freeN(me->mat);
|
2009-07-13 00:40:20 +00:00
|
|
|
ob->mat= me->mat= NULL;
|
|
|
|
ob->matbits= NULL;
|
2008-12-30 13:16:14 +00:00
|
|
|
|
|
|
|
if(totcol) {
|
|
|
|
me->mat= matar;
|
|
|
|
ob->mat= MEM_callocN(sizeof(void *)*totcol, "join obmatar");
|
2009-07-13 00:40:20 +00:00
|
|
|
ob->matbits= MEM_callocN(sizeof(char)*totcol, "join obmatbits");
|
2008-12-30 13:16:14 +00:00
|
|
|
}
|
2009-07-13 00:40:20 +00:00
|
|
|
else
|
|
|
|
MEM_freeN(matar);
|
2008-12-30 13:16:14 +00:00
|
|
|
|
|
|
|
ob->totcol= me->totcol= totcol;
|
|
|
|
ob->colbits= 0;
|
2009-07-13 00:40:20 +00:00
|
|
|
|
2009-11-10 06:29:10 +00:00
|
|
|
if (matmap) MEM_freeN(matmap);
|
2008-12-30 13:16:14 +00:00
|
|
|
|
|
|
|
/* other mesh users */
|
|
|
|
test_object_materials((ID *)me);
|
|
|
|
|
2009-07-13 00:40:20 +00:00
|
|
|
/* free temp copy of destination shapekeys (if applicable) */
|
|
|
|
if(nkey) {
|
|
|
|
// XXX 2.5 Animato
|
|
|
|
#if 0
|
|
|
|
/* free it's ipo too - both are not actually freed from memory yet as ID-blocks */
|
|
|
|
if(nkey->ipo) {
|
|
|
|
free_ipo(nkey->ipo);
|
|
|
|
BLI_remlink(&G.main->ipo, nkey->ipo);
|
|
|
|
MEM_freeN(nkey->ipo);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
free_key(nkey);
|
|
|
|
BLI_remlink(&G.main->key, nkey);
|
|
|
|
MEM_freeN(nkey);
|
|
|
|
}
|
2008-12-30 13:16:14 +00:00
|
|
|
|
2009-07-13 00:40:20 +00:00
|
|
|
DAG_scene_sort(scene); // removed objects, need to rebuild dag before editmode call
|
2008-12-30 13:16:14 +00:00
|
|
|
|
2009-07-13 00:40:20 +00:00
|
|
|
ED_object_enter_editmode(C, EM_WAITCURSOR);
|
- add torus back from 2.4x as an operator
bpy.ops.mesh.primitive_torus_add(major_radius=1, minor_radius=0.25, major_segments=48, minor_segments=16)
- experemental dynamic menus, used for INFO_MT_file, INFO_MT_file_import, INFO_MT_file_export and INFO_MT_mesh_add. these can have items added from python.
eg.
- removed OBJECT_OT_mesh_add, use the python add menu instead.
- made mesh primitive ops - MESH_OT_primitive_plane_add, ...cube_add, etc. work in object mode.
- RNA scene.active_object wrapped
- bugfix [#19466] 2.5: Tweak menu only available for mesh objects added within Edit Mode
ED_object_exit_editmode was always doing an undo push, made this optional using the existing flag - EM_DO_UNDO, called everywhere except when adding primitives.
2009-10-10 21:23:20 +00:00
|
|
|
ED_object_exit_editmode(C, EM_FREEDATA|EM_WAITCURSOR|EM_DO_UNDO);
|
2009-07-13 00:40:20 +00:00
|
|
|
|
|
|
|
WM_event_add_notifier(C, NC_SCENE|ND_OB_ACTIVE, scene);
|
2008-12-30 13:16:14 +00:00
|
|
|
|
2009-07-13 00:40:20 +00:00
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
}
|
2008-12-30 13:16:14 +00:00
|
|
|
|
2009-11-28 04:04:01 +00:00
|
|
|
/*********************** JOIN AS SHAPES ***************************/
|
|
|
|
|
|
|
|
/* Append selected meshes vertex locations as shapes of the active mesh,
|
|
|
|
return 0 if no join is made (error) and 1 of the join is done */
|
|
|
|
|
|
|
|
int join_mesh_shapes_exec(bContext *C, wmOperator *op)
|
|
|
|
{
|
|
|
|
Scene *scene= CTX_data_scene(C);
|
|
|
|
Object *ob= CTX_data_active_object(C);
|
|
|
|
Mesh *me= (Mesh *)ob->data;
|
|
|
|
Mesh *selme=NULL;
|
|
|
|
DerivedMesh *dm=NULL;
|
|
|
|
Key *key=me->key;
|
|
|
|
KeyBlock *kb;
|
|
|
|
int ok=0, nonequal_verts=0;
|
|
|
|
|
|
|
|
CTX_DATA_BEGIN(C, Base*, base, selected_editable_bases) {
|
|
|
|
if (base->object == ob) continue;
|
|
|
|
|
|
|
|
if (base->object->type==OB_MESH) {
|
|
|
|
selme = (Mesh *)base->object->data;
|
|
|
|
|
|
|
|
if (selme->totvert==me->totvert)
|
|
|
|
ok++;
|
|
|
|
else
|
|
|
|
nonequal_verts=1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
CTX_DATA_END;
|
|
|
|
|
|
|
|
if (!ok) {
|
|
|
|
if (nonequal_verts)
|
|
|
|
BKE_report(op->reports, RPT_ERROR, "Selected meshes must have equal numbers of vertices.");
|
|
|
|
else
|
|
|
|
BKE_report(op->reports, RPT_ERROR, "No additional selected meshes with equal vertex count to join.");
|
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(key == NULL) {
|
|
|
|
key= me->key= add_key((ID *)me);
|
|
|
|
key->type= KEY_RELATIVE;
|
|
|
|
|
|
|
|
/* first key added, so it was the basis. initialise it with the existing mesh */
|
2009-12-28 18:03:04 +00:00
|
|
|
kb= add_keyblock(key, NULL);
|
2009-11-28 04:04:01 +00:00
|
|
|
mesh_to_key(me, kb);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* now ready to add new keys from selected meshes */
|
|
|
|
CTX_DATA_BEGIN(C, Base*, base, selected_editable_bases) {
|
|
|
|
if (base->object == ob) continue;
|
|
|
|
|
|
|
|
if(base->object->type==OB_MESH) {
|
|
|
|
selme = (Mesh *)base->object->data;
|
|
|
|
|
|
|
|
if (selme->totvert==me->totvert) {
|
|
|
|
dm = mesh_get_derived_deform(scene, base->object, CD_MASK_BAREMESH);
|
|
|
|
|
|
|
|
if (!dm) continue;
|
|
|
|
|
2009-12-28 18:03:04 +00:00
|
|
|
kb= add_keyblock(key, base->object->id.name+2);
|
2009-11-28 04:04:01 +00:00
|
|
|
|
|
|
|
DM_to_meshkey(dm, me, kb);
|
|
|
|
|
|
|
|
dm->release(dm);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
CTX_DATA_END;
|
|
|
|
|
|
|
|
WM_event_add_notifier(C, NC_SCENE|ND_OB_ACTIVE, scene);
|
|
|
|
|
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
}
|
|
|
|
|
2008-12-30 13:16:14 +00:00
|
|
|
/* ********************** SORT FACES ******************* */
|
|
|
|
|
|
|
|
static void permutate(void *list, int num, int size, int *index)
|
|
|
|
{
|
|
|
|
void *buf;
|
|
|
|
int len;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
len = num * size;
|
|
|
|
|
|
|
|
buf = MEM_mallocN(len, "permutate");
|
|
|
|
memcpy(buf, list, len);
|
|
|
|
|
|
|
|
for (i = 0; i < num; i++) {
|
|
|
|
memcpy((char *)list + (i * size), (char *)buf + (index[i] * size), size);
|
|
|
|
}
|
|
|
|
MEM_freeN(buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* sort faces on view axis */
|
|
|
|
static float *face_sort_floats;
|
|
|
|
static int float_sort(const void *v1, const void *v2)
|
|
|
|
{
|
|
|
|
float x1, x2;
|
|
|
|
|
|
|
|
x1 = face_sort_floats[((int *) v1)[0]];
|
|
|
|
x2 = face_sort_floats[((int *) v2)[0]];
|
|
|
|
|
|
|
|
if( x1 > x2 ) return 1;
|
|
|
|
else if( x1 < x2 ) return -1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void sort_faces(Scene *scene, View3D *v3d)
|
|
|
|
{
|
2.5
View3D has been split now in a local part (RegionView3D) and a
per-area part (old View3D). Currently local is:
- view transform
- camera zoom/offset
- gpencil (todo)
- custom clipping planes
Rest is in Area still, like active camera, draw type, layers,
localview, custom centers, around-settings, transform widget,
gridlines, and so on (mostly stuff as available in header).
To see it work; also added new feature for region split,
press SHIFT+ALT+CTRL+S for four-split.
The idea is to make a preset 4-split, configured to stick
to top/right/front views for three views.
Another cool idea to explore is to then box-clip all drawing
based on these 3 views.
Note about the code:
- currently view3d still stores some depricated settings, to
convert from older files. Not all settings are copied over
though, like custom clip planes or the 'lock view to object'.
- since some view3d ops are now on area level, the operators
for it should keep track of that.
Bugfix in transform: quat initialize in operator-invoke missed
one zero.
Als brought back GE to compile for missing Ipos and channels.
2009-01-19 16:54:41 +00:00
|
|
|
RegionView3D *rv3d= NULL; // get from context
|
2008-12-30 13:16:14 +00:00
|
|
|
Object *ob= OBACT;
|
|
|
|
Mesh *me;
|
|
|
|
CustomDataLayer *layer;
|
|
|
|
int i, *index;
|
|
|
|
short event;
|
|
|
|
float reverse = 1;
|
|
|
|
int ctrl= 0; // XXX
|
|
|
|
|
|
|
|
if(!ob) return;
|
2009-01-02 19:10:35 +00:00
|
|
|
if(scene->obedit) return;
|
2008-12-30 13:16:14 +00:00
|
|
|
if(ob->type!=OB_MESH) return;
|
|
|
|
if (!v3d) return;
|
|
|
|
|
|
|
|
me= ob->data;
|
|
|
|
if(me->totface==0) return;
|
|
|
|
|
|
|
|
event = pupmenu(
|
|
|
|
"Sort Faces (Ctrl to reverse)%t|"
|
|
|
|
"View Axis%x1|"
|
|
|
|
"Cursor Distance%x2|"
|
|
|
|
"Material%x3|"
|
|
|
|
"Selection%x4|"
|
|
|
|
"Randomize%x5");
|
|
|
|
|
|
|
|
if (event==-1) return;
|
|
|
|
|
|
|
|
if(ctrl)
|
|
|
|
reverse = -1;
|
|
|
|
|
|
|
|
/* create index list */
|
|
|
|
index = (int *) MEM_mallocN(sizeof(int) * me->totface, "sort faces");
|
|
|
|
for (i = 0; i < me->totface; i++) {
|
|
|
|
index[i] = i;
|
|
|
|
}
|
|
|
|
|
|
|
|
face_sort_floats = (float *) MEM_mallocN(sizeof(float) * me->totface, "sort faces float");
|
|
|
|
|
|
|
|
/* sort index list instead of faces itself
|
|
|
|
and apply this permutation to all face layers */
|
|
|
|
|
|
|
|
if (event == 5) {
|
|
|
|
/* Random */
|
|
|
|
for(i=0; i<me->totface; i++) {
|
|
|
|
face_sort_floats[i] = BLI_frand();
|
|
|
|
}
|
|
|
|
qsort(index, me->totface, sizeof(int), float_sort);
|
|
|
|
} else {
|
|
|
|
MFace *mf;
|
|
|
|
float vec[3];
|
|
|
|
float mat[4][4];
|
|
|
|
float cur[3];
|
|
|
|
|
|
|
|
if (event == 1)
|
2009-11-10 20:43:45 +00:00
|
|
|
mul_m4_m4m4(mat, OBACT->obmat, rv3d->viewmat); /* apply the view matrix to the object matrix */
|
2008-12-30 13:16:14 +00:00
|
|
|
else if (event == 2) { /* sort from cursor */
|
2009-09-22 04:40:16 +00:00
|
|
|
if( v3d && v3d->localvd ) {
|
2008-12-30 13:16:14 +00:00
|
|
|
VECCOPY(cur, v3d->cursor);
|
|
|
|
} else {
|
|
|
|
VECCOPY(cur, scene->cursor);
|
|
|
|
}
|
2009-11-10 20:43:45 +00:00
|
|
|
invert_m4_m4(mat, OBACT->obmat);
|
|
|
|
mul_m4_v3(mat, cur);
|
2008-12-30 13:16:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
mf= me->mface;
|
|
|
|
for(i=0; i<me->totface; i++, mf++) {
|
|
|
|
|
|
|
|
if (event==3) {
|
|
|
|
face_sort_floats[i] = ((float)mf->mat_nr)*reverse;
|
|
|
|
} else if (event==4) {
|
|
|
|
/*selected first*/
|
|
|
|
if (mf->flag & ME_FACE_SEL) face_sort_floats[i] = 0.0;
|
|
|
|
else face_sort_floats[i] = reverse;
|
|
|
|
} else {
|
|
|
|
/* find the faces center */
|
2009-11-10 20:43:45 +00:00
|
|
|
add_v3_v3v3(vec, (me->mvert+mf->v1)->co, (me->mvert+mf->v2)->co);
|
2008-12-30 13:16:14 +00:00
|
|
|
if (mf->v4) {
|
2009-11-10 20:43:45 +00:00
|
|
|
add_v3_v3v3(vec, vec, (me->mvert+mf->v3)->co);
|
|
|
|
add_v3_v3v3(vec, vec, (me->mvert+mf->v4)->co);
|
|
|
|
mul_v3_fl(vec, 0.25f);
|
2008-12-30 13:16:14 +00:00
|
|
|
} else {
|
2009-11-10 20:43:45 +00:00
|
|
|
add_v3_v3v3(vec, vec, (me->mvert+mf->v3)->co);
|
|
|
|
mul_v3_fl(vec, 1.0f/3.0f);
|
2008-12-30 13:16:14 +00:00
|
|
|
} /* done */
|
|
|
|
|
|
|
|
if (event == 1) { /* sort on view axis */
|
2009-11-10 20:43:45 +00:00
|
|
|
mul_m4_v3(mat, vec);
|
2008-12-30 13:16:14 +00:00
|
|
|
face_sort_floats[i] = vec[2] * reverse;
|
2010-02-08 13:55:31 +00:00
|
|
|
} else if(event == 2) { /* distance from cursor*/
|
2009-11-10 20:43:45 +00:00
|
|
|
face_sort_floats[i] = len_v3v3(cur, vec) * reverse; /* back to front */
|
2008-12-30 13:16:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
qsort(index, me->totface, sizeof(int), float_sort);
|
|
|
|
}
|
|
|
|
|
|
|
|
MEM_freeN(face_sort_floats);
|
|
|
|
|
|
|
|
for(i = 0; i < me->fdata.totlayer; i++) {
|
|
|
|
layer = &me->fdata.layers[i];
|
|
|
|
permutate(layer->data, me->totface, CustomData_sizeof(layer->type), index);
|
|
|
|
}
|
|
|
|
|
|
|
|
MEM_freeN(index);
|
|
|
|
|
2.5
Notifiers
---------
Various fixes for wrong use of notifiers, and some new notifiers
to make things a bit more clear and consistent, with two notable
changes:
* Geometry changes are now done with NC_GEOM, rather than
NC_OBJECT|ND_GEOM_, so an object does need to be available.
* Space data now use NC_SPACE|ND_SPACE_*, instead of data
notifiers or even NC_WINDOW in some cases. Note that NC_SPACE
should only be used for notifying about changes in space data,
we don't want to go back to allqueue(REDRAW..).
Depsgraph
---------
The dependency graph now has a different flush call:
DAG_object_flush_update(scene, ob, flag)
is replaced by:
DAG_id_flush_update(id, flag)
It still works basically the same, one difference is that it now
also accepts object data (e.g. Mesh), again to avoid requiring an
Object to be available. Other ID types will simply do nothing at
the moment.
Docs
----
I made some guidelines for how/when to do which kinds of updates
and notifiers. I can't specify totally exact how to make these
decisions, but these are basically the guidelines I use. So, new
and updated docs are here:
http://wiki.blender.org/index.php/BlenderDev/Blender2.5/NotifiersUpdates
http://wiki.blender.org/index.php/BlenderDev/Blender2.5/DataNotifiers
2009-09-04 20:51:09 +00:00
|
|
|
DAG_id_flush_update(ob->data, OB_RECALC_DATA);
|
2008-12-30 13:16:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* ********************* MESH VERTEX OCTREE LOOKUP ************* */
|
|
|
|
|
|
|
|
/* important note; this is unfinished, needs better API for editmode, and custom threshold */
|
|
|
|
|
|
|
|
#define MOC_RES 8
|
|
|
|
#define MOC_NODE_RES 8
|
2010-01-04 14:59:53 +00:00
|
|
|
#define MOC_THRESH 0.00002f
|
2008-12-30 13:16:14 +00:00
|
|
|
|
|
|
|
typedef struct MocNode {
|
|
|
|
struct MocNode *next;
|
|
|
|
intptr_t index[MOC_NODE_RES];
|
|
|
|
} MocNode;
|
|
|
|
|
|
|
|
static int mesh_octree_get_base_offs(float *co, float *offs, float *div)
|
|
|
|
{
|
|
|
|
int vx, vy, vz;
|
|
|
|
|
|
|
|
vx= floor( (co[0]-offs[0])/div[0] );
|
|
|
|
vy= floor( (co[1]-offs[1])/div[1] );
|
|
|
|
vz= floor( (co[2]-offs[2])/div[2] );
|
|
|
|
|
|
|
|
CLAMP(vx, 0, MOC_RES-1);
|
|
|
|
CLAMP(vy, 0, MOC_RES-1);
|
|
|
|
CLAMP(vz, 0, MOC_RES-1);
|
|
|
|
|
|
|
|
return (vx*MOC_RES*MOC_RES) + vy*MOC_RES + vz;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void mesh_octree_add_node(MocNode **bt, intptr_t index)
|
|
|
|
{
|
|
|
|
if(*bt==NULL) {
|
|
|
|
*bt= MEM_callocN(sizeof(MocNode), "MocNode");
|
|
|
|
(*bt)->index[0]= index;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
int a;
|
|
|
|
for(a=0; a<MOC_NODE_RES; a++) {
|
|
|
|
if((*bt)->index[a]==index)
|
|
|
|
return;
|
|
|
|
else if((*bt)->index[a]==0) {
|
|
|
|
(*bt)->index[a]= index;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
mesh_octree_add_node(&(*bt)->next, index);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void mesh_octree_free_node(MocNode **bt)
|
|
|
|
{
|
|
|
|
if( (*bt)->next ) {
|
|
|
|
mesh_octree_free_node(&(*bt)->next);
|
|
|
|
}
|
|
|
|
MEM_freeN(*bt);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* temporal define, just to make nicer code below */
|
|
|
|
#define MOC_ADDNODE(vx, vy, vz) mesh_octree_add_node(basetable + ((vx)*MOC_RES*MOC_RES) + (vy)*MOC_RES + (vz), index)
|
|
|
|
|
|
|
|
static void mesh_octree_add_nodes(MocNode **basetable, float *co, float *offs, float *div, intptr_t index)
|
|
|
|
{
|
|
|
|
float fx, fy, fz;
|
|
|
|
int vx, vy, vz;
|
|
|
|
|
2009-06-08 20:08:19 +00:00
|
|
|
if (!finite(co[0]) ||
|
|
|
|
!finite(co[1]) ||
|
|
|
|
!finite(co[2])
|
2008-12-30 13:16:14 +00:00
|
|
|
) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
fx= (co[0]-offs[0])/div[0];
|
|
|
|
fy= (co[1]-offs[1])/div[1];
|
|
|
|
fz= (co[2]-offs[2])/div[2];
|
|
|
|
CLAMP(fx, 0.0f, MOC_RES-MOC_THRESH);
|
|
|
|
CLAMP(fy, 0.0f, MOC_RES-MOC_THRESH);
|
|
|
|
CLAMP(fz, 0.0f, MOC_RES-MOC_THRESH);
|
|
|
|
|
|
|
|
vx= floor(fx);
|
|
|
|
vy= floor(fy);
|
|
|
|
vz= floor(fz);
|
|
|
|
|
|
|
|
MOC_ADDNODE(vx, vy, vz);
|
|
|
|
|
|
|
|
if( vx>0 )
|
|
|
|
if( fx-((float)vx)-MOC_THRESH < 0.0f)
|
|
|
|
MOC_ADDNODE(vx-1, vy, vz);
|
|
|
|
if( vx<MOC_RES-2 )
|
|
|
|
if( fx-((float)vx)+MOC_THRESH > 1.0f)
|
|
|
|
MOC_ADDNODE(vx+1, vy, vz);
|
|
|
|
|
|
|
|
if( vy>0 )
|
|
|
|
if( fy-((float)vy)-MOC_THRESH < 0.0f)
|
|
|
|
MOC_ADDNODE(vx, vy-1, vz);
|
|
|
|
if( vy<MOC_RES-2 )
|
|
|
|
if( fy-((float)vy)+MOC_THRESH > 1.0f)
|
|
|
|
MOC_ADDNODE(vx, vy+1, vz);
|
|
|
|
|
|
|
|
if( vz>0 )
|
|
|
|
if( fz-((float)vz)-MOC_THRESH < 0.0f)
|
|
|
|
MOC_ADDNODE(vx, vy, vz-1);
|
|
|
|
if( vz<MOC_RES-2 )
|
|
|
|
if( fz-((float)vz)+MOC_THRESH > 1.0f)
|
|
|
|
MOC_ADDNODE(vx, vy, vz+1);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2009-10-22 09:31:07 +00:00
|
|
|
static intptr_t mesh_octree_find_index(MocNode **bt, MVert *mvert, float *co)
|
2008-12-30 13:16:14 +00:00
|
|
|
{
|
|
|
|
float *vec;
|
|
|
|
int a;
|
|
|
|
|
|
|
|
if(*bt==NULL)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
for(a=0; a<MOC_NODE_RES; a++) {
|
|
|
|
if((*bt)->index[a]) {
|
|
|
|
/* does mesh verts and editmode, code looks potential dangerous, octree should really be filled OK! */
|
2009-10-22 09:31:07 +00:00
|
|
|
if(mvert) {
|
2008-12-30 13:16:14 +00:00
|
|
|
vec= (mvert+(*bt)->index[a]-1)->co;
|
2009-11-10 20:43:45 +00:00
|
|
|
if(compare_v3v3(vec, co, MOC_THRESH))
|
2008-12-30 13:16:14 +00:00
|
|
|
return (*bt)->index[a]-1;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
EditVert *eve= (EditVert *)((*bt)->index[a]);
|
2009-11-10 20:43:45 +00:00
|
|
|
if(compare_v3v3(eve->co, co, MOC_THRESH))
|
2008-12-30 13:16:14 +00:00
|
|
|
return (*bt)->index[a];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else return -1;
|
|
|
|
}
|
|
|
|
if( (*bt)->next)
|
2009-10-22 09:31:07 +00:00
|
|
|
return mesh_octree_find_index(&(*bt)->next, mvert, co);
|
2008-12-30 13:16:14 +00:00
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct {
|
|
|
|
MocNode **table;
|
|
|
|
float offs[3], div[3];
|
2009-10-22 09:31:07 +00:00
|
|
|
} MeshOctree = {NULL, {0, 0, 0}, {0, 0, 0}};
|
2008-12-30 13:16:14 +00:00
|
|
|
|
|
|
|
/* mode is 's' start, or 'e' end, or 'u' use */
|
|
|
|
/* if end, ob can be NULL */
|
|
|
|
intptr_t mesh_octree_table(Object *ob, EditMesh *em, float *co, char mode)
|
|
|
|
{
|
|
|
|
MocNode **bt;
|
|
|
|
|
|
|
|
if(mode=='u') { /* use table */
|
|
|
|
if(MeshOctree.table==NULL)
|
|
|
|
mesh_octree_table(ob, em, NULL, 's');
|
|
|
|
|
|
|
|
if(MeshOctree.table) {
|
|
|
|
Mesh *me= ob->data;
|
|
|
|
bt= MeshOctree.table + mesh_octree_get_base_offs(co, MeshOctree.offs, MeshOctree.div);
|
2009-02-23 17:30:00 +00:00
|
|
|
if(em)
|
2009-10-22 09:31:07 +00:00
|
|
|
return mesh_octree_find_index(bt, NULL, co);
|
2008-12-30 13:16:14 +00:00
|
|
|
else
|
2009-10-22 09:31:07 +00:00
|
|
|
return mesh_octree_find_index(bt, me->mvert, co);
|
2008-12-30 13:16:14 +00:00
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if(mode=='s') { /* start table */
|
|
|
|
Mesh *me= ob->data;
|
|
|
|
float min[3], max[3];
|
|
|
|
|
|
|
|
/* we compute own bounding box and don't reuse ob->bb because
|
|
|
|
* we are using the undeformed coordinates*/
|
|
|
|
INIT_MINMAX(min, max);
|
|
|
|
|
2009-01-10 14:19:14 +00:00
|
|
|
if(em && me->edit_mesh==em) {
|
2008-12-30 13:16:14 +00:00
|
|
|
EditVert *eve;
|
|
|
|
|
|
|
|
for(eve= em->verts.first; eve; eve= eve->next)
|
|
|
|
DO_MINMAX(eve->co, min, max)
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
MVert *mvert;
|
2009-10-22 09:31:07 +00:00
|
|
|
int a;
|
2008-12-30 13:16:14 +00:00
|
|
|
|
2009-10-22 09:31:07 +00:00
|
|
|
for(a=0, mvert= me->mvert; a<me->totvert; a++, mvert++)
|
|
|
|
DO_MINMAX(mvert->co, min, max);
|
2008-12-30 13:16:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* for quick unit coordinate calculus */
|
|
|
|
VECCOPY(MeshOctree.offs, min);
|
|
|
|
MeshOctree.offs[0]-= MOC_THRESH; /* we offset it 1 threshold unit extra */
|
|
|
|
MeshOctree.offs[1]-= MOC_THRESH;
|
|
|
|
MeshOctree.offs[2]-= MOC_THRESH;
|
|
|
|
|
2009-11-10 20:43:45 +00:00
|
|
|
sub_v3_v3v3(MeshOctree.div, max, min);
|
2008-12-30 13:16:14 +00:00
|
|
|
MeshOctree.div[0]+= 2*MOC_THRESH; /* and divide with 2 threshold unit more extra (try 8x8 unit grid on paint) */
|
|
|
|
MeshOctree.div[1]+= 2*MOC_THRESH;
|
|
|
|
MeshOctree.div[2]+= 2*MOC_THRESH;
|
|
|
|
|
2009-11-10 20:43:45 +00:00
|
|
|
mul_v3_fl(MeshOctree.div, 1.0f/MOC_RES);
|
2008-12-30 13:16:14 +00:00
|
|
|
if(MeshOctree.div[0]==0.0f) MeshOctree.div[0]= 1.0f;
|
|
|
|
if(MeshOctree.div[1]==0.0f) MeshOctree.div[1]= 1.0f;
|
|
|
|
if(MeshOctree.div[2]==0.0f) MeshOctree.div[2]= 1.0f;
|
|
|
|
|
|
|
|
if(MeshOctree.table) /* happens when entering this call without ending it */
|
|
|
|
mesh_octree_table(ob, em, co, 'e');
|
|
|
|
|
|
|
|
MeshOctree.table= MEM_callocN(MOC_RES*MOC_RES*MOC_RES*sizeof(void *), "sym table");
|
|
|
|
|
2009-01-10 14:19:14 +00:00
|
|
|
if(em && me->edit_mesh==em) {
|
2008-12-30 13:16:14 +00:00
|
|
|
EditVert *eve;
|
|
|
|
|
|
|
|
for(eve= em->verts.first; eve; eve= eve->next) {
|
|
|
|
mesh_octree_add_nodes(MeshOctree.table, eve->co, MeshOctree.offs, MeshOctree.div, (intptr_t)(eve));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
MVert *mvert;
|
|
|
|
int a;
|
|
|
|
|
2009-10-22 09:31:07 +00:00
|
|
|
for(a=0, mvert= me->mvert; a<me->totvert; a++, mvert++)
|
|
|
|
mesh_octree_add_nodes(MeshOctree.table, mvert->co, MeshOctree.offs, MeshOctree.div, a+1);
|
2008-12-30 13:16:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else if(mode=='e') { /* end table */
|
|
|
|
if(MeshOctree.table) {
|
|
|
|
int a;
|
|
|
|
|
|
|
|
for(a=0, bt=MeshOctree.table; a<MOC_RES*MOC_RES*MOC_RES; a++, bt++) {
|
|
|
|
if(*bt) mesh_octree_free_node(bt);
|
|
|
|
}
|
|
|
|
MEM_freeN(MeshOctree.table);
|
|
|
|
MeshOctree.table= NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int mesh_get_x_mirror_vert(Object *ob, int index)
|
|
|
|
{
|
|
|
|
Mesh *me= ob->data;
|
|
|
|
MVert *mvert;
|
|
|
|
float vec[3];
|
|
|
|
|
2009-10-22 09:31:07 +00:00
|
|
|
mvert= me->mvert+index;
|
|
|
|
vec[0]= -mvert->co[0];
|
|
|
|
vec[1]= mvert->co[1];
|
|
|
|
vec[2]= mvert->co[2];
|
2008-12-30 13:16:14 +00:00
|
|
|
|
|
|
|
return mesh_octree_table(ob, NULL, vec, 'u');
|
|
|
|
}
|
|
|
|
|
|
|
|
EditVert *editmesh_get_x_mirror_vert(Object *ob, EditMesh *em, float *co)
|
|
|
|
{
|
|
|
|
float vec[3];
|
|
|
|
intptr_t poinval;
|
|
|
|
|
|
|
|
/* ignore nan verts */
|
2009-06-08 20:08:19 +00:00
|
|
|
if (!finite(co[0]) ||
|
|
|
|
!finite(co[1]) ||
|
|
|
|
!finite(co[2])
|
2008-12-30 13:16:14 +00:00
|
|
|
)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
vec[0]= -co[0];
|
|
|
|
vec[1]= co[1];
|
|
|
|
vec[2]= co[2];
|
|
|
|
|
|
|
|
poinval= mesh_octree_table(ob, em, vec, 'u');
|
|
|
|
if(poinval != -1)
|
|
|
|
return (EditVert *)(poinval);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static unsigned int mirror_facehash(void *ptr)
|
|
|
|
{
|
|
|
|
MFace *mf= ptr;
|
|
|
|
int v0, v1;
|
|
|
|
|
|
|
|
if(mf->v4) {
|
|
|
|
v0= MIN4(mf->v1, mf->v2, mf->v3, mf->v4);
|
|
|
|
v1= MAX4(mf->v1, mf->v2, mf->v3, mf->v4);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
v0= MIN3(mf->v1, mf->v2, mf->v3);
|
|
|
|
v1= MAX3(mf->v1, mf->v2, mf->v3);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ((v0*39)^(v1*31));
|
|
|
|
}
|
|
|
|
|
|
|
|
static int mirror_facerotation(MFace *a, MFace *b)
|
|
|
|
{
|
|
|
|
if(b->v4) {
|
|
|
|
if(a->v1==b->v1 && a->v2==b->v2 && a->v3==b->v3 && a->v4==b->v4)
|
|
|
|
return 0;
|
|
|
|
else if(a->v4==b->v1 && a->v1==b->v2 && a->v2==b->v3 && a->v3==b->v4)
|
|
|
|
return 1;
|
|
|
|
else if(a->v3==b->v1 && a->v4==b->v2 && a->v1==b->v3 && a->v2==b->v4)
|
|
|
|
return 2;
|
|
|
|
else if(a->v2==b->v1 && a->v3==b->v2 && a->v4==b->v3 && a->v1==b->v4)
|
|
|
|
return 3;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if(a->v1==b->v1 && a->v2==b->v2 && a->v3==b->v3)
|
|
|
|
return 0;
|
|
|
|
else if(a->v3==b->v1 && a->v1==b->v2 && a->v2==b->v3)
|
|
|
|
return 1;
|
|
|
|
else if(a->v2==b->v1 && a->v3==b->v2 && a->v1==b->v3)
|
|
|
|
return 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int mirror_facecmp(void *a, void *b)
|
|
|
|
{
|
|
|
|
return (mirror_facerotation((MFace*)a, (MFace*)b) == -1);
|
|
|
|
}
|
|
|
|
|
|
|
|
int *mesh_get_x_mirror_faces(Object *ob, EditMesh *em)
|
|
|
|
{
|
|
|
|
Mesh *me= ob->data;
|
|
|
|
MVert *mv, *mvert= me->mvert;
|
|
|
|
MFace mirrormf, *mf, *hashmf, *mface= me->mface;
|
|
|
|
GHash *fhash;
|
|
|
|
int *mirrorverts, *mirrorfaces;
|
|
|
|
int a;
|
|
|
|
|
|
|
|
mirrorverts= MEM_callocN(sizeof(int)*me->totvert, "MirrorVerts");
|
|
|
|
mirrorfaces= MEM_callocN(sizeof(int)*2*me->totface, "MirrorFaces");
|
|
|
|
|
|
|
|
mesh_octree_table(ob, em, NULL, 's');
|
|
|
|
|
|
|
|
for(a=0, mv=mvert; a<me->totvert; a++, mv++)
|
|
|
|
mirrorverts[a]= mesh_get_x_mirror_vert(ob, a);
|
|
|
|
|
|
|
|
mesh_octree_table(ob, em, NULL, 'e');
|
|
|
|
|
|
|
|
fhash= BLI_ghash_new(mirror_facehash, mirror_facecmp);
|
|
|
|
for(a=0, mf=mface; a<me->totface; a++, mf++)
|
|
|
|
BLI_ghash_insert(fhash, mf, mf);
|
|
|
|
|
|
|
|
for(a=0, mf=mface; a<me->totface; a++, mf++) {
|
|
|
|
mirrormf.v1= mirrorverts[mf->v3];
|
|
|
|
mirrormf.v2= mirrorverts[mf->v2];
|
|
|
|
mirrormf.v3= mirrorverts[mf->v1];
|
|
|
|
mirrormf.v4= (mf->v4)? mirrorverts[mf->v4]: 0;
|
|
|
|
|
|
|
|
/* make sure v4 is not 0 if a quad */
|
|
|
|
if(mf->v4 && mirrormf.v4==0) {
|
|
|
|
SWAP(int, mirrormf.v1, mirrormf.v3);
|
|
|
|
SWAP(int, mirrormf.v2, mirrormf.v4);
|
|
|
|
}
|
|
|
|
|
|
|
|
hashmf= BLI_ghash_lookup(fhash, &mirrormf);
|
|
|
|
if(hashmf) {
|
|
|
|
mirrorfaces[a*2]= hashmf - mface;
|
|
|
|
mirrorfaces[a*2+1]= mirror_facerotation(&mirrormf, hashmf);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
mirrorfaces[a*2]= -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
BLI_ghash_free(fhash, NULL, NULL);
|
|
|
|
MEM_freeN(mirrorverts);
|
|
|
|
|
|
|
|
return mirrorfaces;
|
|
|
|
}
|