diff options
Diffstat (limited to 'source/blender/src/booleanops.c')
-rw-r--r-- | source/blender/src/booleanops.c | 795 |
1 files changed, 795 insertions, 0 deletions
diff --git a/source/blender/src/booleanops.c b/source/blender/src/booleanops.c new file mode 100644 index 00000000000..5c89dee1a46 --- /dev/null +++ b/source/blender/src/booleanops.c @@ -0,0 +1,795 @@ + +#include <string.h> +/** + * $Id$ + * + * ***** BEGIN GPL/BL DUAL 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. The Blender + * Foundation also sells licenses for use in proprietary software under + * the Blender License. See http://www.blender.org/BL/ for information + * about this. + * + * 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, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL/BL DUAL LICENSE BLOCK ***** + * CSG operations. + */ + + + +#include "MEM_guardedalloc.h" + +#include "BLI_ghash.h" + +#include "DNA_material_types.h" +#include "DNA_mesh_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" + +#include "CSG_BooleanOps.h" + +#include "BKE_global.h" +#include "BKE_mesh.h" +#include "BKE_displist.h" +#include "BKE_object.h" +#include "BKE_booleanops.h" +#include "BKE_utildefines.h" +#include "BKE_library.h" +#include "BKE_material.h" + +#include <math.h> + +// TODO check to see how many of these includes are necessary + +#include "BLI_blenlib.h" +#include "BLI_editVert.h" +#include "BLI_arithb.h" +#include "BLI_linklist.h" +#include "BLI_memarena.h" + + +/** + * Here's the vertex iterator structure used to walk through + * the blender vertex structure. + */ + +typedef struct { + Object *ob; + Mesh *mesh; + int pos; +} VertexIt; + +/** + * Implementations of local vertex iterator functions. + * These describe a blender mesh to the CSG module. + */ + +static + void +VertexIt_Destruct( + CSG_VertexIteratorDescriptor * iterator +){ + if (iterator->it) { + // deallocate memory for iterator + MEM_freeN(iterator->it); + iterator->it = 0; + } + iterator->Done = NULL; + iterator->Fill = NULL; + iterator->Reset = NULL; + iterator->Step = NULL; + iterator->num_elements = 0; + +}; + +static + int +VertexIt_Done( + CSG_IteratorPtr it +){ + VertexIt * iterator = (VertexIt *)it; + return(iterator->pos >= iterator->mesh->totvert); +} + + +static + void +VertexIt_Fill( + CSG_IteratorPtr it, + CSG_IVertex *vert +){ + VertexIt * iterator = (VertexIt *)it; + MVert *verts = iterator->mesh->mvert; + + float global_pos[3]; + + VecMat4MulVecfl( + global_pos, + iterator->ob->obmat, + verts[iterator->pos].co + ); + + vert->position[0] = global_pos[0]; + vert->position[1] = global_pos[1]; + vert->position[2] = global_pos[2]; +} + +static + void +VertexIt_Step( + CSG_IteratorPtr it +){ + VertexIt * iterator = (VertexIt *)it; + iterator->pos ++; +} + +static + void +VertexIt_Reset( + CSG_IteratorPtr it +){ + VertexIt * iterator = (VertexIt *)it; + iterator->pos = 0; +} + +static + void +VertexIt_Construct( + CSG_VertexIteratorDescriptor * output, + Object *ob +){ + + VertexIt *it; + if (output == 0) return; + + // allocate some memory for blender iterator + it = (VertexIt *)(MEM_mallocN(sizeof(VertexIt),"Boolean_VIt")); + if (it == 0) { + return; + } + // assign blender specific variables + it->ob = ob; + it->mesh = ob->data; + + it->pos = 0; + + // assign iterator function pointers. + output->Step = VertexIt_Step; + output->Fill = VertexIt_Fill; + output->Done = VertexIt_Done; + output->Reset = VertexIt_Reset; + output->num_elements = it->mesh->totvert; + output->it = it; +} + +/** + * Blender Face iterator + */ + +typedef struct { + Object *ob; + Mesh *mesh; + int pos; +} FaceIt; + + +static + void +FaceIt_Destruct( + CSG_FaceIteratorDescriptor * iterator +) { + MEM_freeN(iterator->it); + iterator->Done = NULL; + iterator->Fill = NULL; + iterator->Reset = NULL; + iterator->Step = NULL; + iterator->num_elements = 0; +}; + + +static + int +FaceIt_Done( + CSG_IteratorPtr it +) { + // assume CSG_IteratorPtr is of the correct type. + FaceIt * iterator = (FaceIt *)it; + return(iterator->pos >= iterator->mesh->totface); +}; + +static + void +FaceIt_Fill( + CSG_IteratorPtr it, + CSG_IFace *face +){ + // assume CSG_IteratorPtr is of the correct type. + FaceIt * face_it = (FaceIt *)it; + Object *ob = face_it->ob; + MFace *mfaces = face_it->mesh->mface; + TFace *tfaces = face_it->mesh->tface; + int f_index = face_it->pos; + MFace *mface = &mfaces[f_index]; + FaceData *fdata = face->user_face_data; + + if (mface->v3) { + // ignore lines (faces with mface->v3==0) + face->vertex_index[0] = mface->v1; + face->vertex_index[1] = mface->v2; + face->vertex_index[2] = mface->v3; + if (mface->v4) { + face->vertex_index[3] = mface->v4; + face->vertex_number = 4; + } else { + face->vertex_number = 3; + } + } + + fdata->material = give_current_material(ob, mface->mat_nr+1); + + // pack rgba colors. + if (tfaces) { + TFace *tface= &tfaces[f_index]; + int i; + + fdata->tpage = tface->tpage; + fdata->flag = tface->flag; + fdata->transp = tface->transp; + fdata->mode = tface->mode; + fdata->tile = tface->tile; + + for (i=0; i<4; i++) { + FaceVertexData *fvdata= face->user_face_vertex_data[i]; + + fvdata->uv[0] = tface->uv[i][0]; + fvdata->uv[1] = tface->uv[i][1]; + fvdata->color[0] = (float) ((tface->col[i] >> 24) & 0xff); + fvdata->color[1] = (float) ((tface->col[i] >> 16) & 0xff); + fvdata->color[2] = (float) ((tface->col[i] >> 8) & 0xff); + fvdata->color[3] = (float) ((tface->col[i] >> 0) & 0xff); + } + } +}; + + +static + void +FaceIt_Step( + CSG_IteratorPtr it +) { + FaceIt * face_it = (FaceIt *)it; + face_it->pos ++; +}; + +static + void +FaceIt_Reset( + CSG_IteratorPtr it +) { + FaceIt * face_it = (FaceIt *)it; + face_it->pos = 0; +} + +static + void +FaceIt_Construct( + CSG_FaceIteratorDescriptor * output, + Object * ob +){ + + FaceIt *it; + if (output == 0) return; + + // allocate some memory for blender iterator + it = (FaceIt *)(MEM_mallocN(sizeof(FaceIt),"Boolean_FIt")); + if (it == 0) { + return ; + } + // assign blender specific variables + it->ob = ob; + it->mesh = ob->data; + it->pos = 0; + + // assign iterator function pointers. + output->Step = FaceIt_Step; + output->Fill = FaceIt_Fill; + output->Done = FaceIt_Done; + output->Reset = FaceIt_Reset; + output->num_elements = it->mesh->totface; + output->it = it; +}; + + +/** + * Interpolation functions for various user data types. + */ + + int +InterpNoUserData( + void *d1, + void *d2, + void *dnew, + float epsilon +) { + // nothing to do of course. + return 0; +} + + int +InterpFaceVertexData( + void *d1, + void *d2, + void *dnew, + float epsilon +) { + /* XXX, passed backwards, should be fixed inside + * BSP lib I guess. + */ + FaceVertexData *fv1 = d2; + FaceVertexData *fv2 = d1; + FaceVertexData *fvO = dnew; + + fvO->uv[0] = (fv2->uv[0] - fv1->uv[0]) * epsilon + fv1->uv[0]; + fvO->uv[1] = (fv2->uv[1] - fv1->uv[1]) * epsilon + fv1->uv[1]; + fvO->color[0] = (fv2->color[0] - fv1->color[0]) * epsilon + fv1->color[0]; + fvO->color[1] = (fv2->color[1] - fv1->color[1]) * epsilon + fv1->color[1]; + fvO->color[2] = (fv2->color[2] - fv1->color[2]) * epsilon + fv1->color[2]; + fvO->color[3] = (fv2->color[3] - fv1->color[3]) * epsilon + fv1->color[3]; + + return 0; +} + + + +/** + * Assumes mesh is valid and forms part of a fresh + * blender object. + */ + + + + int +NewBooleanMesh( + struct Base * base, + struct Base * base_select, + int int_op_type +){ + Mesh *me2 = get_mesh(base_select->object); + Mesh *me = get_mesh(base->object); + Mesh *me_new = NULL; + Object *ob; + int free_tface1,free_tface2; + + float inv_mat[4][4]; + int success = 0; + // build and fill new descriptors for these meshes + CSG_VertexIteratorDescriptor vd_1; + CSG_VertexIteratorDescriptor vd_2; + CSG_FaceIteratorDescriptor fd_1; + CSG_FaceIteratorDescriptor fd_2; + + CSG_MeshPropertyDescriptor mpd1,mpd2; + + // work out the operation they chose and pick the appropriate + // enum from the csg module. + + CSG_OperationType op_type; + + if (me == NULL || me2 == NULL) return 0; + + switch (int_op_type) { + case 1 : op_type = e_csg_intersection; break; + case 2 : op_type = e_csg_union; break; + case 3 : op_type = e_csg_difference; break; + case 4 : op_type = e_csg_classify; break; + default : op_type = e_csg_intersection; + } + + // Here is the section where we describe the properties of + // both meshes to the bsp module. + + if (me->mcol != NULL) { + // Then this mesh has vertex colors only + // well this is awkward because there is no equivalent + // test_index_mface just for vertex colors! + // as a temporary hack we can convert these vertex colors + // into tfaces do the operation and turn them back again. + + // create some memory for the tfaces. + me->tface = (TFace *)MEM_callocN(sizeof(TFace)*me->totface,"BooleanOps_TempTFace"); + mcol_to_tface(me,1); + free_tface1 = 1; + } else { + free_tface1 = 0; + } + + mpd1.user_face_vertex_data_size = 0; + mpd1.user_data_size = sizeof(FaceData); + + if (me->tface) { + mpd1.user_face_vertex_data_size = sizeof(FaceVertexData); + } + + // same for mesh2 + + if (me2->mcol != NULL) { + // create some memory for the tfaces. + me2->tface = (TFace *)MEM_callocN(sizeof(TFace)*me2->totface,"BooleanOps_TempTFace"); + mcol_to_tface(me2,1); + free_tface2 = 1; + } else { + free_tface2 = 0; + } + + mpd2.user_face_vertex_data_size = 0; + mpd2.user_data_size = sizeof(FaceData); + + if (me2->tface) { + mpd2.user_face_vertex_data_size = sizeof(FaceVertexData); + } + + ob = base->object; + + // we map the final object back into object 1's (ob) + // local coordinate space. For this we need to compute + // the inverse transform from global to local. + + Mat4Invert(inv_mat,ob->obmat); + + // make a boolean operation; + { + CSG_BooleanOperation * bool_op = CSG_NewBooleanFunction(); + CSG_MeshPropertyDescriptor output_mpd = CSG_DescibeOperands(bool_op,mpd1,mpd2); + // analyse the result and choose mesh descriptors accordingly + int output_type; + if (output_mpd.user_face_vertex_data_size) { + output_type = 1; + } else { + output_type = 0; + } + + BuildMeshDescriptors( + base->object, + &fd_1, + &vd_1 + ); + BuildMeshDescriptors( + base_select->object, + &fd_2, + &vd_2 + ); + + // perform the operation + + if (output_type == 0) { + + success = + CSG_PerformBooleanOperation( + bool_op, + op_type, + fd_1,vd_1,fd_2,vd_2, + InterpNoUserData + ); + } else { + success = + CSG_PerformBooleanOperation( + bool_op, + op_type, + fd_1,vd_1,fd_2,vd_2, + InterpFaceVertexData + ); + } + + if (success) { + // descriptions of the output; + CSG_VertexIteratorDescriptor vd_o; + CSG_FaceIteratorDescriptor fd_o; + + // Create a new blender mesh object - using 'base' as + // a template for the new object. + Object * ob_new= AddNewBlenderMesh(base); + + // get the output descriptors + + CSG_OutputFaceDescriptor(bool_op,&fd_o); + CSG_OutputVertexDescriptor(bool_op,&vd_o); + + me_new = ob_new->data; + // iterate through results of operation and insert into new object + // see subsurf.c + + ConvertCSGDescriptorsToMeshObject( + ob_new, + &output_mpd, + &fd_o, + &vd_o, + inv_mat + ); + + // initialize the object + tex_space_mesh(me_new); + + // free up the memory + + CSG_FreeVertexDescriptor(&vd_o); + CSG_FreeFaceDescriptor(&fd_o); + } + + CSG_FreeBooleanOperation(bool_op); + bool_op = NULL; + + } + + // We may need to map back the tfaces to mcols here. + if (free_tface1) { + tface_to_mcol(me); + MEM_freeN(me->tface); + me->tface = NULL; + } + if (free_tface2) { + tface_to_mcol(me2); + MEM_freeN(me2->tface); + me2->tface = NULL; + } + + if (free_tface1 && free_tface2) { + // then we need to map the output tfaces into mcols + if (me_new) { + tface_to_mcol(me_new); + MEM_freeN(me_new->tface); + me_new->tface = NULL; + } + } + + FreeMeshDescriptors(&fd_1,&vd_1); + FreeMeshDescriptors(&fd_2,&vd_2); + + return success; +} + + + Object * +AddNewBlenderMesh( + Base *base +){ + Mesh *old_me; + Base *basen; + Object *ob_new; + + // now create a new blender object. + // duplicating all the settings from the previous object + // to the new one. + ob_new= copy_object(base->object); + + // Ok we don't want to use the actual data from the + // last object, the above function incremented the + // number of users, so decrement it here. + old_me= ob_new->data; + old_me->id.us--; + + // Now create a new base to add into the linked list of + // vase objects. + + basen= MEM_mallocN(sizeof(Base), "duplibase"); + *basen= *base; + BLI_addhead(&G.scene->base, basen); /* addhead: anders oneindige lus */ + basen->object= ob_new; + basen->flag &= ~SELECT; + + // Initialize the mesh data associated with this object. + ob_new->data= add_mesh(); + G.totmesh++; + + // Finally assign the object type. + ob_new->type= OB_MESH; + + return ob_new; +}; + + + +/** + * + * External interface + * + * This function builds a blender mesh using the output information from + * the CSG module. It declares all the necessary blender cruft and + * fills in the vertex and face arrays. + */ + int +ConvertCSGDescriptorsToMeshObject( + Object *ob, + CSG_MeshPropertyDescriptor *props, + CSG_FaceIteratorDescriptor *face_it, + CSG_VertexIteratorDescriptor *vertex_it, + float parinv[][4] +){ + Mesh *me = ob->data; + FaceVertexData *user_face_vertex_data; + GHash *material_hash; + CSG_IVertex vert; + CSG_IFace face; + MVert *insert_pos; + MFace *mfaces; + TFace *tfaces; + int fi_insert_pos, nmaterials; + + // create some memory for the Iface according to output mesh props. + + if (face_it == NULL || vertex_it == NULL || props == NULL || me == NULL) { + return 0; + } + if (vertex_it->num_elements > 65000) return 0; + + // initialize the face structure for readback + + face.user_face_data = MEM_callocN(sizeof(FaceData),"BooleanOp_IFaceData"); + + if (props->user_face_vertex_data_size) { + user_face_vertex_data = MEM_callocN(sizeof(FaceVertexData)*4,"BooleanOp_IFaceData"); + face.user_face_vertex_data[0] = &user_face_vertex_data[0]; + face.user_face_vertex_data[1] = &user_face_vertex_data[1]; + face.user_face_vertex_data[2] = &user_face_vertex_data[2]; + face.user_face_vertex_data[3] = &user_face_vertex_data[3]; + } else { + user_face_vertex_data = NULL; + } + + // create memory for the vertex array. + + me->mvert = MEM_callocN(sizeof(MVert) * vertex_it->num_elements,"BooleanOp_VertexArray"); + me->mface = MEM_callocN(sizeof(MFace) * face_it->num_elements,"BooleanOp_FaceArray"); + + if (user_face_vertex_data) { + me->tface = MEM_callocN(sizeof(TFace) * face_it->num_elements,"BooleanOp_TFaceArray"); + if (me->tface == NULL) return 0; + } else { + me->tface = NULL; + } + + if (me->mvert == NULL || me->mface == NULL) return 0; + + insert_pos = me->mvert; + mfaces = me->mface; + tfaces = me->tface; + + fi_insert_pos = 0; + + // step through the iterators. + + while (!vertex_it->Done(vertex_it->it)) { + vertex_it->Fill(vertex_it->it,&vert); + + // map output vertex into insert_pos + // and transform at by parinv at the same time. + + VecMat4MulVecfl( + insert_pos->co, + parinv, + vert.position + ); + insert_pos ++; + vertex_it->Step(vertex_it->it); + } + + me->totvert = vertex_it->num_elements; + + // a hash table to remap materials to indices with + material_hash = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp); + nmaterials = 0; + + while (!face_it->Done(face_it->it)) { + MFace *mface = &mfaces[fi_insert_pos]; + FaceData *fdata; + + face_it->Fill(face_it->it,&face); + + // cheat CSG never dumps out quads. + + mface->v1 = face.vertex_index[0]; + mface->v2 = face.vertex_index[1]; + mface->v3 = face.vertex_index[2]; + mface->v4 = 0; + + mface->edcode = ME_V1V2|ME_V2V3|ME_V3V4|ME_V4V1; + mface->puno = 0; + mface->mat_nr = 0; + mface->flag = 0; + + /* HACK, perform material to index mapping using a general + * hash table, just tuck the int into a void *. + */ + + fdata = face.user_face_data; + if (!BLI_ghash_haskey(material_hash, fdata->material)) { + int matnr = nmaterials++; + BLI_ghash_insert(material_hash, fdata->material, (void*) matnr); + assign_material(ob, fdata->material, matnr+1); + } + mface->mat_nr = (int) BLI_ghash_lookup(material_hash, fdata->material); + + // grab the vertex colors and texture cos and dump them into the tface. + + if (tfaces) { + TFace *tface= &tfaces[fi_insert_pos]; + int i; + + // copy all the tface settings back + tface->tpage = fdata->tpage; + tface->flag = fdata->flag; + tface->transp = fdata->transp; + tface->mode = fdata->mode; + tface->tile = fdata->tile; + + for (i=0; i<4; i++) { + FaceVertexData *fvdata = face.user_face_vertex_data[i]; + float *color = fvdata->color; + + tface->uv[i][0] = fvdata->uv[0]; + tface->uv[i][1] = fvdata->uv[1]; + tface->col[i] = + ((((unsigned int)floor(color[0] + 0.5f)) & 0xff) << 24) | + ((((unsigned int)floor(color[1] + 0.5f)) & 0xff) << 16) | + ((((unsigned int)floor(color[2] + 0.5f)) & 0xff) << 8) | + ((((unsigned int)floor(color[3] + 0.5f)) & 0xff) << 0); + } + + test_index_face(mface, tface, 3); + } else { + test_index_mface(mface, 3); + } + + fi_insert_pos++; + face_it->Step(face_it->it); + } + + BLI_ghash_free(material_hash, NULL, NULL); + + me->totface = face_it->num_elements; + // thats it! + + if (user_face_vertex_data) { + MEM_freeN(user_face_vertex_data); + } + MEM_freeN(face.user_face_data); + + return 1; +} + + void +BuildMeshDescriptors( + struct Object *ob, + struct CSG_FaceIteratorDescriptor * face_it, + struct CSG_VertexIteratorDescriptor * vertex_it +){ + VertexIt_Construct(vertex_it,ob); + FaceIt_Construct(face_it,ob); +} + + void +FreeMeshDescriptors( + struct CSG_FaceIteratorDescriptor *face_it, + struct CSG_VertexIteratorDescriptor *vertex_it +){ + VertexIt_Destruct(vertex_it); + FaceIt_Destruct(face_it); +} + |