Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNicholas Bishop <nicholasbishop@gmail.com>2007-12-26 12:39:15 +0300
committerNicholas Bishop <nicholasbishop@gmail.com>2007-12-26 12:39:15 +0300
commit88e71a5b799e852d6803a8c9075c4b46305e2478 (patch)
tree44486d6b994b41aa9911dee0c7dc6070135e70a9 /source/blender/blenkernel
parentb0b3a69c19fd60e6c48a877b22cab0ba48882c20 (diff)
== Multires ==
Cleaned up bad level calls for multires; moved most of multires functions to blenkern, where they should have been in the first place. Functionality of the tool is unchanged.
Diffstat (limited to 'source/blender/blenkernel')
-rw-r--r--source/blender/blenkernel/BKE_bad_level_calls.h9
-rw-r--r--source/blender/blenkernel/BKE_multires.h67
-rw-r--r--source/blender/blenkernel/bad_level_call_stubs/stubs.c8
-rw-r--r--source/blender/blenkernel/intern/DerivedMesh.c3
-rw-r--r--source/blender/blenkernel/intern/mesh.c3
-rw-r--r--source/blender/blenkernel/intern/multires-firstlevel.c409
-rw-r--r--source/blender/blenkernel/intern/multires.c1393
7 files changed, 1872 insertions, 20 deletions
diff --git a/source/blender/blenkernel/BKE_bad_level_calls.h b/source/blender/blenkernel/BKE_bad_level_calls.h
index 02e2f799103..923504dfeab 100644
--- a/source/blender/blenkernel/BKE_bad_level_calls.h
+++ b/source/blender/blenkernel/BKE_bad_level_calls.h
@@ -207,15 +207,6 @@ void post_layer_create(struct VLayer *vlayer);
void post_layer_destroy(struct VLayer *vlayer);
void post_server_add(void);
-/* multires.c */
-struct Multires;
-struct MultiresLevel;
-struct MultiresLevel *multires_level_n(struct Multires *mr, int n);
-void multires_free(struct Multires *mr);
-void multires_set_level(struct Object *ob, struct Mesh *me, const int render);
-void multires_update_levels(struct Mesh *me, const int render);
-void multires_calc_level_maps(struct MultiresLevel *lvl);
-struct Multires *multires_copy(struct Multires *orig);
/* sculptmode.c */
struct SculptData;
void sculpt_reset_curve(struct SculptData *sd);
diff --git a/source/blender/blenkernel/BKE_multires.h b/source/blender/blenkernel/BKE_multires.h
new file mode 100644
index 00000000000..54bf801fd25
--- /dev/null
+++ b/source/blender/blenkernel/BKE_multires.h
@@ -0,0 +1,67 @@
+/*
+ * $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,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * The Original Code is Copyright (C) 2007 by Nicholas Bishop
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): none yet.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+struct CustomData;
+struct EditMesh;
+struct Multires;
+struct MultiresLevel;
+struct Mesh;
+struct Object;
+
+/* Level access */
+struct MultiresLevel *current_level(struct Multires *mr);
+struct MultiresLevel *multires_level_n(struct Multires *mr, int n);
+
+/* Level control */
+void multires_add_level(struct Object *ob, struct Mesh *me, const char subdiv_type);
+void multires_set_level(struct Object *ob, struct Mesh *me, const int render);
+void multires_free_level(struct MultiresLevel *lvl);
+
+void multires_edge_level_update(struct Object *ob, struct Mesh *me);
+
+void multires_free(struct Multires *mr);
+struct Multires *multires_copy(struct Multires *orig);
+void multires_create(struct Mesh *me);
+
+/* CustomData */
+void multires_delete_layer(struct Mesh *me, struct CustomData *cd, const int type, int n);
+void multires_add_layer(struct Mesh *me, struct CustomData *cd, const int type, const int n);
+void multires_del_lower_customdata(struct Multires *mr, struct MultiresLevel *cr_lvl);
+/* After adding or removing vcolor layers, run this */
+void multires_load_cols(struct Mesh *me);
+
+/* Private (used in multires-firstlevel.c) */
+void multires_level_to_mesh(struct Object *ob, struct Mesh *me, const int render);
+void multires_update_levels(struct Mesh *me, const int render);
+void multires_update_first_level(struct Mesh *me, struct EditMesh *em);
+void multires_update_customdata(struct MultiresLevel *lvl1, struct CustomData *src,
+ struct CustomData *dst, const int type);
+void multires_customdata_to_mesh(struct Mesh *me, struct EditMesh *em,
+ struct MultiresLevel *lvl, struct CustomData *src,
+ struct CustomData *dst, const int type);
diff --git a/source/blender/blenkernel/bad_level_call_stubs/stubs.c b/source/blender/blenkernel/bad_level_call_stubs/stubs.c
index e0aa288c7b2..8885d4db760 100644
--- a/source/blender/blenkernel/bad_level_call_stubs/stubs.c
+++ b/source/blender/blenkernel/bad_level_call_stubs/stubs.c
@@ -310,13 +310,7 @@ void post_geometry_free_constraint(struct VNode *vnode) {}
void post_layer_create(struct VLayer *vlayer) {}
void post_layer_destroy(struct VLayer *vlayer) {}
void post_server_add(void) {}
- /* Multires/sculpt stubs */
-struct MultiresLevel *multires_level_n(struct Multires *mr, int n) {return NULL;}
-void multires_free(struct Multires *mr) {}
-void multires_set_level(struct Object *ob, struct Mesh *me, const int render) {}
-void multires_update_levels(struct Mesh *me, const int render) {}
-void multires_calc_level_maps(struct MultiresLevel *lvl) {}
-struct Multires *multires_copy(struct Multires *orig) {return NULL;}
+ /* sculpt stubs */
void sculpt_reset_curve(struct SculptData *sd) {}
void sculptmode_init(struct Scene *sce) {}
void sculptmode_free_all(struct Scene *sce) {}
diff --git a/source/blender/blenkernel/intern/DerivedMesh.c b/source/blender/blenkernel/intern/DerivedMesh.c
index 9d51fc645ba..aada60be21b 100644
--- a/source/blender/blenkernel/intern/DerivedMesh.c
+++ b/source/blender/blenkernel/intern/DerivedMesh.c
@@ -74,6 +74,7 @@
#include "BKE_material.h"
#include "BKE_modifier.h"
#include "BKE_mesh.h"
+#include "BKE_multires.h"
#include "BKE_object.h"
#include "BKE_subsurf.h"
#include "BKE_texture.h"
@@ -87,8 +88,6 @@
#include "BIF_gl.h"
#include "BIF_glutil.h"
-#include "multires.h"
-
// headers for fluidsim bobj meshes
#include <stdlib.h>
#include "LBM_fluidsim.h"
diff --git a/source/blender/blenkernel/intern/mesh.c b/source/blender/blenkernel/intern/mesh.c
index 56b1fe7e75b..6156b2bed89 100644
--- a/source/blender/blenkernel/intern/mesh.c
+++ b/source/blender/blenkernel/intern/mesh.c
@@ -63,6 +63,7 @@
#include "BKE_DerivedMesh.h"
#include "BKE_global.h"
#include "BKE_mesh.h"
+#include "BKE_multires.h"
#include "BKE_subsurf.h"
#include "BKE_displist.h"
#include "BKE_library.h"
@@ -83,8 +84,6 @@
#include "BLI_editVert.h"
#include "BLI_arithb.h"
-#include "multires.h"
-
int update_realtime_texture(MTFace *tface, double time)
{
Image *ima;
diff --git a/source/blender/blenkernel/intern/multires-firstlevel.c b/source/blender/blenkernel/intern/multires-firstlevel.c
new file mode 100644
index 00000000000..778dd6f9c77
--- /dev/null
+++ b/source/blender/blenkernel/intern/multires-firstlevel.c
@@ -0,0 +1,409 @@
+/*
+ * $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,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * The Original Code is Copyright (C) 2006 by Nicholas Bishop
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): none yet.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ *
+ * Deals with the first-level data in multires (edge flags, weights, and UVs)
+ *
+ * multires.h
+ *
+ */
+
+#include "DNA_customdata_types.h"
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
+
+#include "BIF_editmesh.h"
+
+#include "BKE_customdata.h"
+#include "BKE_global.h"
+#include "BKE_mesh.h"
+#include "BKE_multires.h"
+
+#include "BLI_editVert.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "blendef.h"
+
+#include <string.h>
+
+MDeformVert *subdivide_dverts(MDeformVert *src, MultiresLevel *lvl);
+MTFace *subdivide_mtfaces(MTFace *src, MultiresLevel *lvl);
+void multires_update_edge_flags(Mesh *me, EditMesh *em);
+void eed_to_medge_flag(EditEdge *eed, short *flag, char *crease);
+
+/*********** Generic ***********/
+
+CustomDataMask cdmask(const int type)
+{
+ if(type == CD_MDEFORMVERT)
+ return CD_MASK_MDEFORMVERT;
+ else if(type == CD_MTFACE)
+ return CD_MASK_MTFACE;
+ return -1;
+}
+
+char type_ok(const int type)
+{
+ return (type == CD_MDEFORMVERT) || (type == CD_MTFACE);
+}
+
+/* Copy vdata or fdata from Mesh or EditMesh to Multires. */
+void multires_update_customdata(MultiresLevel *lvl1, CustomData *src, CustomData *dst, const int type)
+{
+ if(src && dst && type_ok(type)) {
+ const int tot= (type == CD_MDEFORMVERT ? lvl1->totvert : lvl1->totface);
+ int i;
+
+ CustomData_free(dst, tot);
+
+ if(CustomData_has_layer(src, type)) {
+ if(G.obedit) {
+ EditVert *eve= G.editMesh->verts.first;
+ EditFace *efa= G.editMesh->faces.first;
+ CustomData_copy(src, dst, cdmask(type), CD_CALLOC, tot);
+ for(i=0; i<tot; ++i) {
+ if(type == CD_MDEFORMVERT) {
+ CustomData_from_em_block(&G.editMesh->vdata, dst, eve->data, i);
+ eve= eve->next;
+ }
+ else if(type == CD_MTFACE) {
+ CustomData_from_em_block(&G.editMesh->fdata, dst, efa->data, i);
+ efa= efa->next;
+ }
+ }
+ }
+ else
+ CustomData_copy(src, dst, cdmask(type), CD_DUPLICATE, tot);
+ }
+ }
+}
+
+/* Uses subdivide_dverts or subdivide_mtfaces to subdivide src to match lvl_end. Does not free src. */
+void *subdivide_customdata_to_level(void *src, MultiresLevel *lvl_start,
+ MultiresLevel *lvl_end, const int type)
+{
+ if(src && lvl_start && lvl_end && type_ok(type)) {
+ MultiresLevel *lvl;
+ void *cr_data= NULL, *pr_data= NULL;
+
+ pr_data= src;
+ for(lvl= lvl_start; lvl && lvl != lvl_end; lvl= lvl->next) {
+ if(type == CD_MDEFORMVERT)
+ cr_data= subdivide_dverts(pr_data, lvl);
+ else if(type == CD_MTFACE)
+ cr_data= subdivide_mtfaces(pr_data, lvl);
+
+ /* Free previous subdivision level's data */
+ if(lvl != lvl_start) {
+ if(type == CD_MDEFORMVERT)
+ free_dverts(pr_data, lvl->totvert);
+ else if(type == CD_MTFACE)
+ MEM_freeN(pr_data);
+ }
+
+ pr_data= cr_data;
+ cr_data= NULL;
+ }
+
+ return pr_data;
+ }
+
+ return NULL;
+}
+
+/* Directly copy src into dst (handles both Mesh and EditMesh) */
+void customdata_to_mesh(Mesh *me, EditMesh *em, CustomData *src, CustomData *dst, const int tot, const int type)
+{
+ if(me && me->mr && src && dst && type_ok(type)) {
+ if(em) {
+ int i;
+ EditVert *eve= em->verts.first;
+ EditFace *efa= em->faces.first;
+ CustomData_copy(src, dst, cdmask(type), CD_CALLOC, 0);
+
+ for(i=0; i<tot; ++i) {
+ if(type == CD_MDEFORMVERT) {
+ CustomData_to_em_block(src, dst, i, &eve->data);
+ eve= eve->next;
+ }
+ else if(type == CD_MTFACE) {
+ CustomData_to_em_block(src, dst, i, &efa->data);
+ efa= efa->next;
+ }
+ }
+ } else {
+ CustomData_merge(src, dst, cdmask(type), CD_DUPLICATE, tot);
+ }
+ }
+}
+
+/* Subdivide vdata or fdata from Multires into either Mesh or EditMesh. */
+void multires_customdata_to_mesh(Mesh *me, EditMesh *em, MultiresLevel *lvl, CustomData *src,
+ CustomData *dst, const int type)
+{
+ if(me && me->mr && lvl && src && dst && type_ok(type) &&
+ CustomData_has_layer(src, type)) {
+ const int tot= (type == CD_MDEFORMVERT ? lvl->totvert : lvl->totface);
+ if(lvl == me->mr->levels.first) {
+ customdata_to_mesh(me, em, src, dst, tot, type);
+ }
+ else {
+ CustomData cdf;
+ const int count = CustomData_number_of_layers(src, type);
+ int i;
+
+ /* Construct a new CustomData containing the subdivided data */
+ CustomData_copy(src, &cdf, cdmask(type), CD_ASSIGN, tot);
+ for(i=0; i<count; ++i) {
+ void *layer= CustomData_get_layer_n(&cdf, type, i);
+ CustomData_set_layer_n(&cdf, type, i,
+ subdivide_customdata_to_level(layer, me->mr->levels.first, lvl, type));
+ }
+
+ customdata_to_mesh(me, em, &cdf, dst, tot, type);
+ CustomData_free(&cdf, tot);
+ }
+ }
+}
+
+/* Subdivide the first-level customdata up to cr_lvl, then delete the original data */
+void multires_del_lower_customdata(Multires *mr, MultiresLevel *cr_lvl)
+{
+ MultiresLevel *lvl1= mr->levels.first;
+ MDeformVert *dverts= NULL;
+ CustomData cdf;
+ int i;
+
+ /* dverts */
+ dverts= subdivide_customdata_to_level(CustomData_get(&mr->vdata, 0, CD_MDEFORMVERT),
+ lvl1, cr_lvl, CD_MDEFORMVERT);
+ if(dverts) {
+ CustomData_free_layers(&mr->vdata, CD_MDEFORMVERT, lvl1->totvert);
+ CustomData_add_layer(&mr->vdata, CD_MDEFORMVERT, CD_ASSIGN, dverts, cr_lvl->totvert);
+ }
+
+ /* mtfaces */
+ CustomData_copy(&mr->fdata, &cdf, CD_MASK_MTFACE, CD_ASSIGN, cr_lvl->totface);
+ for(i=0; i<CustomData_number_of_layers(&mr->fdata, CD_MTFACE); ++i) {
+ MTFace *mtfaces=
+ subdivide_customdata_to_level(CustomData_get_layer_n(&mr->fdata, CD_MTFACE, i),
+ lvl1, cr_lvl, CD_MTFACE);
+ if(mtfaces)
+ CustomData_set_layer_n(&cdf, CD_MTFACE, i, mtfaces);
+ }
+
+ CustomData_free(&mr->fdata, lvl1->totface);
+ mr->fdata= cdf;
+}
+
+/* Update all special first-level data, if the first-level is active */
+void multires_update_first_level(Mesh *me, EditMesh *em)
+{
+ if(me && me->mr && me->mr->current == 1) {
+ multires_update_customdata(me->mr->levels.first, em ? &em->vdata : &me->vdata,
+ &me->mr->vdata, CD_MDEFORMVERT);
+ multires_update_customdata(me->mr->levels.first, em ? &em->fdata : &me->fdata,
+ &me->mr->fdata, CD_MTFACE);
+ multires_update_edge_flags(me, em);
+ }
+}
+
+/*********** Multires.edge_flags ***********/
+void multires_update_edge_flags(Mesh *me, EditMesh *em)
+{
+ MultiresLevel *lvl= me->mr->levels.first;
+ EditEdge *eed= NULL;
+ int i;
+
+ if(em) eed= em->edges.first;
+ for(i=0; i<lvl->totedge; ++i) {
+ if(em) {
+ me->mr->edge_flags[i]= 0;
+ eed_to_medge_flag(eed, &me->mr->edge_flags[i], &me->mr->edge_creases[i]);
+ eed= eed->next;
+ }
+ else {
+ me->mr->edge_flags[i]= me->medge[i].flag;
+ me->mr->edge_creases[i]= me->medge[i].crease;
+ }
+ }
+}
+
+
+
+/*********** Multires.vdata ***********/
+
+/* MDeformVert */
+
+/* Add each weight from in to out. Scale each weight by w. */
+void multires_add_dvert(MDeformVert *out, const MDeformVert *in, const float w)
+{
+ if(out && in) {
+ int i, j;
+ char found;
+
+ for(i=0; i<in->totweight; ++i) {
+ found= 0;
+ for(j=0; j<out->totweight; ++j) {
+ if(out->dw[j].def_nr==in->dw[i].def_nr) {
+ out->dw[j].weight += in->dw[i].weight * w;
+ found= 1;
+ }
+ }
+ if(!found) {
+ MDeformWeight *newdw= MEM_callocN(sizeof(MDeformWeight)*(out->totweight+1),
+ "multires dvert");
+ if(out->dw) {
+ memcpy(newdw, out->dw, sizeof(MDeformWeight)*out->totweight);
+ MEM_freeN(out->dw);
+ }
+
+ out->dw= newdw;
+ out->dw[out->totweight].weight= in->dw[i].weight * w;
+ out->dw[out->totweight].def_nr= in->dw[i].def_nr;
+
+ ++out->totweight;
+ }
+ }
+ }
+}
+
+/* Takes an input array of dverts and subdivides them (linear) using the topology of lvl */
+MDeformVert *subdivide_dverts(MDeformVert *src, MultiresLevel *lvl)
+{
+ if(lvl && lvl->next) {
+ MDeformVert *out = MEM_callocN(sizeof(MDeformVert)*lvl->next->totvert, "dvert prop array");
+ int i, j;
+
+ /* Copy lower level */
+ for(i=0; i<lvl->totvert; ++i)
+ multires_add_dvert(&out[i], &src[i], 1);
+ /* Edge verts */
+ for(i=0; i<lvl->totedge; ++i) {
+ for(j=0; j<2; ++j)
+ multires_add_dvert(&out[lvl->totvert+i], &src[lvl->edges[i].v[j]],0.5);
+ }
+
+ /* Face verts */
+ for(i=0; i<lvl->totface; ++i) {
+ for(j=0; j<(lvl->faces[i].v[3]?4:3); ++j)
+ multires_add_dvert(&out[lvl->totvert + lvl->totedge + i],
+ &src[lvl->faces[i].v[j]],
+ lvl->faces[i].v[3]?0.25:(1.0f/3.0f));
+ }
+
+ return out;
+ }
+
+ return NULL;
+}
+
+
+
+/*********** Multires.fdata ***********/
+
+/* MTFace */
+
+void multires_uv_avg2(float out[2], const float a[2], const float b[2])
+{
+ int i;
+ for(i=0; i<2; ++i)
+ out[i] = (a[i] + b[i]) / 2.0f;
+}
+
+/* Takes an input array of mtfaces and subdivides them (linear) using the topology of lvl */
+MTFace *subdivide_mtfaces(MTFace *src, MultiresLevel *lvl)
+{
+ if(lvl && lvl->next) {
+ MTFace *out= MEM_callocN(sizeof(MultiresColFace)*lvl->next->totface,"Multirescolfaces");
+ int i, j, curf;
+
+ for(i=0, curf=0; i<lvl->totface; ++i) {
+ const char sides= lvl->faces[i].v[3]?4:3;
+ float cntr[2]= {0, 0};
+
+ /* Find average uv coord of the current face */
+ for(j=0; j<sides; ++j) {
+ cntr[0]+= src[i].uv[j][0];
+ cntr[1]+= src[i].uv[j][1];
+ }
+ cntr[0]/= sides;
+ cntr[1]/= sides;
+
+ for(j=0; j<sides; ++j, ++curf) {
+ out[curf]= src[i];
+
+ multires_uv_avg2(out[curf].uv[0], src[i].uv[j], src[i].uv[j==0?sides-1:j-1]);
+
+ out[curf].uv[1][0]= src[i].uv[j][0];
+ out[curf].uv[1][1]= src[i].uv[j][1];
+
+ multires_uv_avg2(out[curf].uv[2], src[i].uv[j], src[i].uv[j==sides-1?0:j+1]);
+
+ out[curf].uv[3][0]= cntr[0];
+ out[curf].uv[3][1]= cntr[1];
+ }
+ }
+
+ return out;
+ }
+
+ return NULL;
+}
+
+void multires_delete_layer(Mesh *me, CustomData *cd, const int type, int n)
+{
+ if(me && me->mr && cd) {
+ MultiresLevel *lvl1= me->mr->levels.first;
+
+ multires_update_levels(me, 0);
+
+ CustomData_set_layer_active(cd, type, n);
+ CustomData_free_layer_active(cd, type, lvl1->totface);
+
+ multires_level_to_mesh(OBACT, me, 0);
+ }
+}
+
+void multires_add_layer(Mesh *me, CustomData *cd, const int type, const int n)
+{
+ if(me && me->mr && cd) {
+ multires_update_levels(me, 0);
+
+ if(CustomData_has_layer(cd, type))
+ CustomData_add_layer(cd, type, CD_DUPLICATE, CustomData_get_layer(cd, type),
+ current_level(me->mr)->totface);
+ else
+ CustomData_add_layer(cd, type, CD_DEFAULT, NULL, current_level(me->mr)->totface);
+
+ CustomData_set_layer_active(cd, type, n);
+ multires_level_to_mesh(OBACT, me, 0);
+ }
+}
diff --git a/source/blender/blenkernel/intern/multires.c b/source/blender/blenkernel/intern/multires.c
new file mode 100644
index 00000000000..2b50f37e7a1
--- /dev/null
+++ b/source/blender/blenkernel/intern/multires.c
@@ -0,0 +1,1393 @@
+/*
+ * $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,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * The Original Code is Copyright (C) 2007 by Nicholas Bishop
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): none yet.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_key_types.h"
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_object_types.h"
+#include "DNA_vec_types.h"
+
+#include "BDR_sculptmode.h"
+
+#include "BIF_editmesh.h"
+
+#include "BLI_arithb.h"
+#include "BLI_blenlib.h"
+#include "BLI_editVert.h"
+
+#include "BKE_customdata.h"
+#include "BKE_depsgraph.h"
+#include "BKE_global.h"
+#include "BKE_multires.h"
+
+#include "blendef.h"
+#include "editmesh.h"
+
+#include <math.h>
+
+/* Returns the active multires level (currently applied to the mesh) */
+MultiresLevel *current_level(Multires *mr)
+{
+ return BLI_findlink(&mr->levels, mr->current - 1);
+}
+
+/* Returns the nth multires level, starting at 1 */
+MultiresLevel *multires_level_n(Multires *mr, int n)
+{
+ if(mr)
+ return BLI_findlink(&mr->levels, n - 1);
+ else
+ return NULL;
+}
+
+/* Free and clear the temporary connectivity data */
+static void multires_free_temp_data(MultiresLevel *lvl)
+{
+ if(lvl) {
+ if(lvl->edge_boundary_states) MEM_freeN(lvl->edge_boundary_states);
+ if(lvl->vert_edge_map) MEM_freeN(lvl->vert_edge_map);
+ if(lvl->vert_face_map) MEM_freeN(lvl->vert_face_map);
+ if(lvl->map_mem) MEM_freeN(lvl->map_mem);
+
+ lvl->edge_boundary_states = NULL;
+ lvl->vert_edge_map = lvl->vert_face_map = NULL;
+ lvl->map_mem = NULL;
+ }
+}
+
+/* Does not actually free lvl itself */
+void multires_free_level(MultiresLevel *lvl)
+{
+ if(lvl) {
+ if(lvl->faces) MEM_freeN(lvl->faces);
+ if(lvl->edges) MEM_freeN(lvl->edges);
+ if(lvl->colfaces) MEM_freeN(lvl->colfaces);
+
+ multires_free_temp_data(lvl);
+ }
+}
+
+void multires_free(Multires *mr)
+{
+ if(mr) {
+ MultiresLevel* lvl= mr->levels.first;
+
+ /* Free the first-level data */
+ if(lvl) {
+ CustomData_free(&mr->vdata, lvl->totvert);
+ CustomData_free(&mr->fdata, lvl->totface);
+ MEM_freeN(mr->edge_flags);
+ MEM_freeN(mr->edge_creases);
+ }
+
+ while(lvl) {
+ multires_free_level(lvl);
+ lvl= lvl->next;
+ }
+
+ MEM_freeN(mr->verts);
+
+ BLI_freelistN(&mr->levels);
+
+ MEM_freeN(mr);
+ }
+}
+
+static MultiresLevel *multires_level_copy(MultiresLevel *orig)
+{
+ if(orig) {
+ MultiresLevel *lvl= MEM_dupallocN(orig);
+
+ lvl->next= lvl->prev= NULL;
+ lvl->faces= MEM_dupallocN(orig->faces);
+ lvl->colfaces= MEM_dupallocN(orig->colfaces);
+ lvl->edges= MEM_dupallocN(orig->edges);
+ lvl->edge_boundary_states = NULL;
+ lvl->vert_edge_map= lvl->vert_face_map= NULL;
+ lvl->map_mem= NULL;
+
+ return lvl;
+ }
+ return NULL;
+}
+
+Multires *multires_copy(Multires *orig)
+{
+ const CustomDataMask vdata_mask= CD_MASK_MDEFORMVERT;
+
+ if(orig) {
+ Multires *mr= MEM_dupallocN(orig);
+ MultiresLevel *lvl;
+
+ mr->levels.first= mr->levels.last= NULL;
+
+ for(lvl= orig->levels.first; lvl; lvl= lvl->next)
+ BLI_addtail(&mr->levels, multires_level_copy(lvl));
+
+ mr->verts= MEM_dupallocN(orig->verts);
+
+ lvl= mr->levels.first;
+ if(lvl) {
+ CustomData_copy(&orig->vdata, &mr->vdata, vdata_mask, CD_DUPLICATE, lvl->totvert);
+ CustomData_copy(&orig->fdata, &mr->fdata, CD_MASK_MTFACE, CD_DUPLICATE, lvl->totface);
+ mr->edge_flags= MEM_dupallocN(orig->edge_flags);
+ mr->edge_creases= MEM_dupallocN(orig->edge_creases);
+ }
+
+ return mr;
+ }
+ return NULL;
+}
+
+static void multires_get_vert(MVert *out, EditVert *eve, MVert *m, int i)
+{
+ if(eve) {
+ VecCopyf(out->co, eve->co);
+ out->flag= 0;
+ if(eve->f & SELECT) out->flag |= 1;
+ if(eve->h) out->flag |= ME_HIDE;
+ eve->tmp.l= i;
+ }
+ else
+ *out= *m;
+}
+
+void eed_to_medge_flag(EditEdge *eed, short *flag, char *crease)
+{
+ if(!eed || !flag) return;
+
+ /* Would be nice if EditMesh edge flags could be unified with Mesh flags! */
+ *flag= (eed->f & SELECT) | ME_EDGERENDER;
+ if(eed->f2<2) *flag |= ME_EDGEDRAW;
+ if(eed->f2==0) *flag |= ME_LOOSEEDGE;
+ if(eed->sharp) *flag |= ME_SHARP;
+ if(eed->seam) *flag |= ME_SEAM;
+ if(eed->h & EM_FGON) *flag |= ME_FGON;
+ if(eed->h & 1) *flag |= ME_HIDE;
+
+ *crease= (char)(255.0*eed->crease);
+}
+
+static void multires_get_edge(MultiresEdge *e, EditEdge *eed, MEdge *m, short *flag, char *crease)
+{
+ if(eed) {
+ e->v[0]= eed->v1->tmp.l;
+ e->v[1]= eed->v2->tmp.l;
+ eed_to_medge_flag(eed, flag, crease);
+ } else {
+ e->v[0]= m->v1;
+ e->v[1]= m->v2;
+ *flag= m->flag;
+ *crease= m->crease;
+ }
+}
+
+static void multires_get_face(MultiresFace *f, EditFace *efa, MFace *m)
+{
+ if(efa) {
+ MFace tmp;
+ int j;
+ tmp.v1= efa->v1->tmp.l;
+ tmp.v2= efa->v2->tmp.l;
+ tmp.v3= efa->v3->tmp.l;
+ tmp.v4= 0;
+ if(efa->v4) tmp.v4= efa->v4->tmp.l;
+ test_index_face(&tmp, NULL, 0, efa->v4?4:3);
+ for(j=0; j<4; ++j) f->v[j]= (&tmp.v1)[j];
+
+ /* Flags */
+ f->flag= efa->flag;
+ if(efa->f & 1) f->flag |= ME_FACE_SEL;
+ else f->flag &= ~ME_FACE_SEL;
+ if(efa->h) f->flag |= ME_HIDE;
+ f->mat_nr= efa->mat_nr;
+ } else {
+ f->v[0]= m->v1;
+ f->v[1]= m->v2;
+ f->v[2]= m->v3;
+ f->v[3]= m->v4;
+ f->flag= m->flag;
+ f->mat_nr= m->mat_nr;
+ }
+}
+
+/* For manipulating vertex colors / uvs */
+static void mcol_to_multires(MultiresColFace *mrf, MCol *mcol)
+{
+ char i;
+ for(i=0; i<4; ++i) {
+ mrf->col[i].a= mcol[i].a;
+ mrf->col[i].r= mcol[i].r;
+ mrf->col[i].g= mcol[i].g;
+ mrf->col[i].b= mcol[i].b;
+ }
+}
+
+/* 1 <= count <= 4 */
+static void multires_col_avg(MultiresCol *avg, MultiresCol cols[4], char count)
+{
+ unsigned i;
+ avg->a= avg->r= avg->g= avg->b= 0;
+ for(i=0; i<count; ++i) {
+ avg->a+= cols[i].a;
+ avg->r+= cols[i].r;
+ avg->g+= cols[i].g;
+ avg->b+= cols[i].b;
+ }
+ avg->a/= count;
+ avg->r/= count;
+ avg->g/= count;
+ avg->b/= count;
+}
+
+static void multires_col_avg2(MultiresCol *avg, MultiresCol *c1, MultiresCol *c2)
+{
+ MultiresCol in[2];
+ in[0]= *c1;
+ in[1]= *c2;
+ multires_col_avg(avg,in,2);
+}
+
+void multires_load_cols(Mesh *me)
+{
+ MultiresLevel *lvl= BLI_findlink(&me->mr->levels,me->mr->current-1), *cur;
+ EditMesh *em= G.obedit ? G.editMesh : NULL;
+ CustomData *src= em ? &em->fdata : &me->fdata;
+ EditFace *efa= NULL;
+ unsigned i,j;
+
+ if(!CustomData_has_layer(src, CD_MCOL) && !CustomData_has_layer(src, CD_MTFACE)) return;
+
+ /* Add texcol data */
+ for(cur= me->mr->levels.first; cur; cur= cur->next)
+ if(!cur->colfaces)
+ cur->colfaces= MEM_callocN(sizeof(MultiresColFace)*cur->totface,"ColFaces");
+
+ me->mr->use_col= CustomData_has_layer(src, CD_MCOL);
+
+ if(em) efa= em->faces.first;
+ for(i=0; i<lvl->totface; ++i) {
+ MultiresColFace *f= &lvl->colfaces[i];
+
+ if(me->mr->use_col)
+ mcol_to_multires(f, em ? CustomData_em_get(src, efa->data, CD_MCOL) : &me->mcol[i*4]);
+
+ if(em) efa= efa->next;
+ }
+
+ /* Update higher levels */
+ lvl= lvl->next;
+ while(lvl) {
+ MultiresColFace *cf= lvl->colfaces;
+ for(i=0; i<lvl->prev->totface; ++i) {
+ const char sides= lvl->prev->faces[i].v[3]?4:3;
+ MultiresCol cntr;
+
+ /* Find average color of 4 (or 3 for triangle) verts */
+ multires_col_avg(&cntr,lvl->prev->colfaces[i].col,sides);
+
+ for(j=0; j<sides; ++j) {
+ MultiresColFace *pf= &lvl->prev->colfaces[i];
+
+ multires_col_avg2(&cf->col[0],
+ &pf->col[j],
+ &pf->col[j==0?sides-1:j-1]);
+ cf->col[1]= pf->col[j];
+ multires_col_avg2(&cf->col[2],
+ &pf->col[j],
+ &pf->col[j==sides-1?0:j+1]);
+ cf->col[3]= cntr;
+
+ ++cf;
+ }
+ }
+ lvl= lvl->next;
+ }
+
+ /* Update lower levels */
+ lvl= me->mr->levels.last;
+ lvl= lvl->prev;
+ while(lvl) {
+ unsigned curf= 0;
+ for(i=0; i<lvl->totface; ++i) {
+ MultiresFace *f= &lvl->faces[i];
+ for(j=0; j<(f->v[3]?4:3); ++j) {
+ lvl->colfaces[i].col[j]= lvl->next->colfaces[curf].col[1];
+ ++curf;
+ }
+ }
+ lvl= lvl->prev;
+ }
+}
+
+void multires_create(Mesh *me)
+{
+ MultiresLevel *lvl;
+ EditMesh *em= G.obedit ? G.editMesh : NULL;
+ EditVert *eve= NULL;
+ EditFace *efa= NULL;
+ EditEdge *eed= NULL;
+ int i;
+
+ lvl= MEM_callocN(sizeof(MultiresLevel), "multires level");
+
+ if(me->pv) sculptmode_pmv_off(me);
+
+ me->mr= MEM_callocN(sizeof(Multires), "multires data");
+
+ BLI_addtail(&me->mr->levels,lvl);
+ me->mr->current= 1;
+ me->mr->level_count= 1;
+ me->mr->edgelvl= 1;
+ me->mr->pinlvl= 1;
+ me->mr->renderlvl= 1;
+
+ /* Load mesh (or editmesh) into multires data */
+
+ /* Load vertices and vdata (MDeformVerts) */
+ lvl->totvert= em ? BLI_countlist(&em->verts) : me->totvert;
+ me->mr->verts= MEM_callocN(sizeof(MVert)*lvl->totvert,"multires verts");
+ multires_update_customdata(me->mr->levels.first, em ? &em->vdata : &me->vdata,
+ &me->mr->vdata, CD_MDEFORMVERT);
+ if(em) eve= em->verts.first;
+ for(i=0; i<lvl->totvert; ++i) {
+ multires_get_vert(&me->mr->verts[i], eve, &me->mvert[i], i);
+ if(em) eve= eve->next;
+ }
+
+ /* Load faces and fdata (MTFaces) */
+ lvl->totface= em ? BLI_countlist(&em->faces) : me->totface;
+ lvl->faces= MEM_callocN(sizeof(MultiresFace)*lvl->totface,"multires faces");
+ multires_update_customdata(me->mr->levels.first, em ? &em->fdata : &me->fdata,
+ &me->mr->fdata, CD_MTFACE);
+ if(em) efa= em->faces.first;
+ for(i=0; i<lvl->totface; ++i) {
+ multires_get_face(&lvl->faces[i], efa, &me->mface[i]);
+ if(em) efa= efa->next;
+ }
+
+ /* Load edges and edge_flags */
+ lvl->totedge= em ? BLI_countlist(&em->edges) : me->totedge;
+ lvl->edges= MEM_callocN(sizeof(MultiresEdge)*lvl->totedge,"multires edges");
+ me->mr->edge_flags= MEM_callocN(sizeof(short)*lvl->totedge, "multires edge flags");
+ me->mr->edge_creases= MEM_callocN(sizeof(short)*lvl->totedge, "multires edge creases");
+ if(em) eed= em->edges.first;
+ for(i=0; i<lvl->totedge; ++i) {
+ multires_get_edge(&lvl->edges[i], eed, &me->medge[i], &me->mr->edge_flags[i], &me->mr->edge_creases[i]);
+ if(em) eed= eed->next;
+ }
+
+ multires_load_cols(me);
+}
+
+typedef struct MultiresMapNode {
+ struct MultiresMapNode *next, *prev;
+ unsigned Index;
+} MultiresMapNode;
+
+/* Produces temporary connectivity data for the multires lvl */
+static void multires_calc_temp_data(MultiresLevel *lvl)
+{
+ unsigned i, j, emax;
+ MultiresMapNode *indexnode= NULL;
+
+ lvl->map_mem= MEM_mallocN(sizeof(MultiresMapNode)*(lvl->totedge*2 + lvl->totface*4), "map_mem");
+ indexnode= lvl->map_mem;
+
+ /* edge map */
+ lvl->vert_edge_map= MEM_callocN(sizeof(ListBase)*lvl->totvert,"vert_edge_map");
+ for(i=0; i<lvl->totedge; ++i) {
+ for(j=0; j<2; ++j, ++indexnode) {
+ indexnode->Index= i;
+ BLI_addtail(&lvl->vert_edge_map[lvl->edges[i].v[j]], indexnode);
+ }
+ }
+
+ /* face map */
+ lvl->vert_face_map= MEM_callocN(sizeof(ListBase)*lvl->totvert,"vert_face_map");
+ for(i=0; i<lvl->totface; ++i){
+ for(j=0; j<(lvl->faces[i].v[3]?4:3); ++j, ++indexnode) {
+ indexnode->Index= i;
+ BLI_addtail(&lvl->vert_face_map[lvl->faces[i].v[j]], indexnode);
+ }
+ }
+
+ /* edge boundaries */
+ emax = (lvl->prev ? (lvl->prev->totedge * 2) : lvl->totedge);
+ lvl->edge_boundary_states= MEM_callocN(sizeof(char)*lvl->totedge, "edge_boundary_states");
+ for(i=0; i<emax; ++i) {
+ MultiresMapNode *n1= lvl->vert_face_map[lvl->edges[i].v[0]].first;
+ unsigned total= 0;
+
+ lvl->edge_boundary_states[i] = 1;
+ while(n1 && lvl->edge_boundary_states[i] == 1) {
+ MultiresMapNode *n2= lvl->vert_face_map[lvl->edges[i].v[1]].first;
+ while(n2) {
+ if(n1->Index == n2->Index) {
+ ++total;
+
+ if(total > 1) {
+ lvl->edge_boundary_states[i] = 0;
+ break;
+ }
+ }
+
+ n2= n2->next;
+ }
+ n1= n1->next;
+ }
+ }
+}
+
+/* CATMULL-CLARK
+ ============= */
+
+typedef struct MultiApplyData {
+ /* Smooth faces */
+ float *corner1, *corner2, *corner3, *corner4;
+ char quad;
+
+ /* Smooth edges */
+ char boundary;
+ float edge_face_neighbor_midpoints_accum[3];
+ unsigned edge_face_neighbor_midpoints_total;
+ float *endpoint1, *endpoint2;
+
+ /* Smooth verts */
+ /* uses 'char boundary' */
+ float *original;
+ int edge_count;
+ float vert_face_neighbor_midpoints_average[3];
+ float vert_edge_neighbor_midpoints_average[3];
+ float boundary_edges_average[3];
+} MultiApplyData;
+
+/* Simply averages the four corners of a polygon. */
+static float catmullclark_smooth_face(MultiApplyData *data, const unsigned i)
+{
+ const float total= data->corner1[i]+data->corner2[i]+data->corner3[i];
+ return data->quad ? (total+data->corner4[i])/4 : total/3;
+}
+
+static float catmullclark_smooth_edge(MultiApplyData *data, const unsigned i)
+{
+ float accum= 0;
+ unsigned count= 2;
+
+ accum+= data->endpoint1[i] + data->endpoint2[i];
+
+ if(!data->boundary) {
+ accum+= data->edge_face_neighbor_midpoints_accum[i];
+ count+= data->edge_face_neighbor_midpoints_total;
+ }
+
+ return accum / count;
+}
+
+static float catmullclark_smooth_vert(MultiApplyData *data, const unsigned i)
+{
+ if(data->boundary) {
+ return data->original[i]*0.75 + data->boundary_edges_average[i]*0.25;
+ } else {
+ return (data->vert_face_neighbor_midpoints_average[i] +
+ 2*data->vert_edge_neighbor_midpoints_average[i] +
+ data->original[i]*(data->edge_count-3))/data->edge_count;
+ }
+}
+
+
+
+/* Call func count times, passing in[i] as the input and storing the output in out[i] */
+static void multi_apply(float *out, MultiApplyData *data,
+ const unsigned count, float (*func)(MultiApplyData *, const unsigned))
+{
+ unsigned i;
+ for(i=0; i<count; ++i)
+ out[i]= func(data,i);
+}
+
+static short multires_vert_is_boundary(MultiresLevel *lvl, unsigned v)
+{
+ MultiresMapNode *node= lvl->vert_edge_map[v].first;
+ while(node) {
+ if(lvl->edge_boundary_states[node->Index])
+ return 1;
+ node= node->next;
+ }
+ return 0;
+}
+
+#define GET_FLOAT(array, i, j, stride) (((float*)((char*)(array)+((i)*(stride))))[(j)])
+
+static void edge_face_neighbor_midpoints_accum(MultiApplyData *data, MultiresLevel *lvl,
+ void *array, const char stride, const MultiresEdge *e)
+{
+ ListBase *neighbors1= &lvl->vert_face_map[e->v[0]];
+ ListBase *neighbors2= &lvl->vert_face_map[e->v[1]];
+ MultiresMapNode *n1, *n2;
+ unsigned j,count= 0;
+ float *out= data->edge_face_neighbor_midpoints_accum;
+
+ out[0]=out[1]=out[2]= 0;
+
+ for(n1= neighbors1->first; n1; n1= n1->next) {
+ for(n2= neighbors2->first; n2; n2= n2->next) {
+ if(n1->Index == n2->Index) {
+ for(j=0; j<3; ++j)
+ out[j]+= GET_FLOAT(array,lvl->faces[n1->Index].mid,j,stride);
+ ++count;
+ }
+ }
+ }
+
+ data->edge_face_neighbor_midpoints_total= count;
+}
+
+static void vert_face_neighbor_midpoints_average(MultiApplyData *data, MultiresLevel *lvl,
+ void *array, const char stride, const unsigned i)
+{
+ ListBase *neighbors= &lvl->vert_face_map[i];
+ MultiresMapNode *n1;
+ unsigned j,count= 0;
+ float *out= data->vert_face_neighbor_midpoints_average;
+
+ out[0]=out[1]=out[2]= 0;
+
+ for(n1= neighbors->first; n1; n1= n1->next) {
+ for(j=0; j<3; ++j)
+ out[j]+= GET_FLOAT(array,lvl->faces[n1->Index].mid,j,stride);
+ ++count;
+ }
+ for(j=0; j<3; ++j) out[j]/= count;
+}
+
+static void vert_edge_neighbor_midpoints_average(MultiApplyData *data, MultiresLevel *lvl,
+ void *array, const char stride, const unsigned i)
+{
+ ListBase *neighbors= &lvl->vert_edge_map[i];
+ MultiresMapNode *n1;
+ unsigned j,count= 0;
+ float *out= data->vert_edge_neighbor_midpoints_average;
+
+ out[0]=out[1]=out[2]= 0;
+
+ for(n1= neighbors->first; n1; n1= n1->next) {
+ for(j=0; j<3; ++j)
+ out[j]+= (GET_FLOAT(array,lvl->edges[n1->Index].v[0],j,stride) +
+ GET_FLOAT(array,lvl->edges[n1->Index].v[1],j,stride)) / 2;
+ ++count;
+ }
+ for(j=0; j<3; ++j) out[j]/= count;
+}
+
+static void boundary_edges_average(MultiApplyData *data, MultiresLevel *lvl,
+ void *array, const char stride, const unsigned i)
+{
+ ListBase *neighbors= &lvl->vert_edge_map[i];
+ MultiresMapNode *n1;
+ unsigned j,count= 0;
+ float *out= data->boundary_edges_average;
+
+ out[0]=out[1]=out[2]= 0;
+
+ for(n1= neighbors->first; n1; n1= n1->next) {
+ const MultiresEdge *e= &lvl->edges[n1->Index];
+ const unsigned end= e->v[0]==i ? e->v[1] : e->v[0];
+
+ if(lvl->edge_boundary_states[n1->Index]) {
+ for(j=0; j<3; ++j)
+ out[j]+= GET_FLOAT(array,end,j,stride);
+ ++count;
+ }
+ }
+ for(j=0; j<3; ++j) out[j]/= count;
+}
+
+/* END CATMULL-CLARK
+ ================= */
+
+/* Update vertex locations and vertex flags */
+static void multires_update_vertices(Mesh *me, EditMesh *em)
+{
+ MultiresLevel *cr_lvl= current_level(me->mr), *pr_lvl= NULL,
+ *last_lvl= me->mr->levels.last;
+ vec3f *pr_deltas= NULL, *cr_deltas= NULL, *swap_deltas= NULL;
+ EditVert *eve= NULL;
+ MultiApplyData data;
+ int i, j;
+
+ /* Prepare deltas */
+ pr_deltas= MEM_callocN(sizeof(vec3f)*last_lvl->totvert, "multires deltas 1");
+ cr_deltas= MEM_callocN(sizeof(vec3f)*last_lvl->totvert, "multires deltas 2");
+
+ /* Calculate initial deltas -- current mesh subtracted from current level*/
+ if(em) eve= em->verts.first;
+ for(i=0; i<cr_lvl->totvert; ++i) {
+ if(em) {
+ VecSubf(&cr_deltas[i].x, eve->co, me->mr->verts[i].co);
+ eve= eve->next;
+ } else
+ VecSubf(&cr_deltas[i].x, me->mvert[i].co, me->mr->verts[i].co);
+ }
+
+
+ /* Copy current level's vertex flags and clear the rest */
+ if(em) eve= em->verts.first;
+ for(i=0; i < last_lvl->totvert; ++i) {
+ if(i < cr_lvl->totvert) {
+ MVert mvflag;
+ multires_get_vert(&mvflag, eve, &me->mvert[i], i);
+ if(em) eve= eve->next;
+ me->mr->verts[i].flag= mvflag.flag;
+ }
+ else
+ me->mr->verts[i].flag= 0;
+ }
+
+ /* If already on the highest level, copy current verts (including flags) into current level */
+ if(cr_lvl == last_lvl) {
+ if(em)
+ eve= em->verts.first;
+ for(i=0; i<cr_lvl->totvert; ++i) {
+ multires_get_vert(&me->mr->verts[i], eve, &me->mvert[i], i);
+ if(em) eve= eve->next;
+ }
+ }
+
+ /* Update higher levels */
+ pr_lvl= BLI_findlink(&me->mr->levels,me->mr->current-1);
+ cr_lvl= pr_lvl->next;
+ while(cr_lvl) {
+ multires_calc_temp_data(pr_lvl);
+
+ /* Swap the old/new deltas */
+ swap_deltas= pr_deltas;
+ pr_deltas= cr_deltas;
+ cr_deltas= swap_deltas;
+
+ /* Calculate and add new deltas
+ ============================ */
+ for(i=0; i<pr_lvl->totface; ++i) {
+ const MultiresFace *f= &pr_lvl->faces[i];
+ data.corner1= &pr_deltas[f->v[0]].x;
+ data.corner2= &pr_deltas[f->v[1]].x;
+ data.corner3= &pr_deltas[f->v[2]].x;
+ data.corner4= &pr_deltas[f->v[3]].x;
+ data.quad= f->v[3] ? 1 : 0;
+ multi_apply(&cr_deltas[f->mid].x, &data, 3, catmullclark_smooth_face);
+
+ for(j=0; j<(data.quad?4:3); ++j)
+ me->mr->verts[f->mid].flag |= me->mr->verts[f->v[j]].flag;
+ }
+
+ for(i=0; i<pr_lvl->totedge; ++i) {
+ const MultiresEdge *e= &pr_lvl->edges[i];
+ data.boundary= pr_lvl->edge_boundary_states[i];
+ edge_face_neighbor_midpoints_accum(&data,pr_lvl,cr_deltas,sizeof(vec3f),e);
+ data.endpoint1= &pr_deltas[e->v[0]].x;
+ data.endpoint2= &pr_deltas[e->v[1]].x;
+ multi_apply(&cr_deltas[e->mid].x, &data, 3, catmullclark_smooth_edge);
+
+ for(j=0; j<2; ++j)
+ me->mr->verts[e->mid].flag |= me->mr->verts[e->v[j]].flag;
+ }
+
+ for(i=0; i<pr_lvl->totvert; ++i) {
+ data.boundary= multires_vert_is_boundary(pr_lvl,i);
+ data.original= &pr_deltas[i].x;
+ data.edge_count= BLI_countlist(&pr_lvl->vert_edge_map[i]);
+ if(data.boundary)
+ boundary_edges_average(&data,pr_lvl,pr_deltas,sizeof(vec3f),i);
+ else {
+ vert_face_neighbor_midpoints_average(&data,pr_lvl,cr_deltas,sizeof(vec3f),i);
+ vert_edge_neighbor_midpoints_average(&data,pr_lvl,pr_deltas,sizeof(vec3f),i);
+ }
+ multi_apply(&cr_deltas[i].x, &data, 3, catmullclark_smooth_vert);
+ }
+
+ /* Apply deltas to vertex locations */
+ for(i=0; (cr_lvl == last_lvl) && (i < cr_lvl->totvert); ++i) {
+ VecAddf(me->mr->verts[i].co,
+ me->mr->verts[i].co,
+ &cr_deltas[i].x);
+ }
+
+ multires_free_temp_data(pr_lvl);
+
+ pr_lvl= pr_lvl->next;
+ cr_lvl= cr_lvl->next;
+ }
+ if(pr_deltas) MEM_freeN(pr_deltas);
+ if(cr_deltas) MEM_freeN(cr_deltas);
+
+}
+
+static void multires_update_faces(Mesh *me, EditMesh *em)
+{
+ MultiresLevel *cr_lvl= current_level(me->mr), *pr_lvl= NULL,
+ *last_lvl= me->mr->levels.last;
+ char *pr_flag_damaged= NULL, *cr_flag_damaged= NULL, *or_flag_damaged= NULL,
+ *pr_mat_damaged= NULL, *cr_mat_damaged= NULL, *or_mat_damaged= NULL, *swap= NULL;
+ EditFace *efa= NULL;
+ unsigned i,j,curf;
+
+ /* Find for each face whether flag/mat has changed */
+ pr_flag_damaged= MEM_callocN(sizeof(char) * last_lvl->totface, "flag_damaged 1");
+ cr_flag_damaged= MEM_callocN(sizeof(char) * last_lvl->totface, "flag_damaged 1");
+ pr_mat_damaged= MEM_callocN(sizeof(char) * last_lvl->totface, "mat_damaged 1");
+ cr_mat_damaged= MEM_callocN(sizeof(char) * last_lvl->totface, "mat_damaged 1");
+ if(em) efa= em->faces.first;
+ for(i=0; i<cr_lvl->totface; ++i) {
+ MultiresFace mftmp;
+ multires_get_face(&mftmp, efa, &me->mface[i]);
+ if(cr_lvl->faces[i].flag != mftmp.flag)
+ cr_flag_damaged[i]= 1;
+ if(cr_lvl->faces[i].mat_nr != mftmp.mat_nr)
+ cr_mat_damaged[i]= 1;
+
+ /* Update current level */
+ cr_lvl->faces[i].flag= mftmp.flag;
+ cr_lvl->faces[i].mat_nr= mftmp.mat_nr;
+
+ if(em) efa= efa->next;
+ }
+ or_flag_damaged= MEM_dupallocN(cr_flag_damaged);
+ or_mat_damaged= MEM_dupallocN(cr_mat_damaged);
+
+ /* Update lower levels */
+ cr_lvl= cr_lvl->prev;
+ while(cr_lvl) {
+ swap= pr_flag_damaged;
+ pr_flag_damaged= cr_flag_damaged;
+ cr_flag_damaged= swap;
+
+ swap= pr_mat_damaged;
+ pr_mat_damaged= cr_mat_damaged;
+ cr_mat_damaged= swap;
+
+ curf= 0;
+ for(i=0; i<cr_lvl->totface; ++i) {
+ const int sides= cr_lvl->faces[i].v[3] ? 4 : 3;
+
+ /* Check damages */
+ for(j=0; j<sides; ++j, ++curf) {
+ if(pr_flag_damaged[curf]) {
+ cr_lvl->faces[i].flag= cr_lvl->next->faces[curf].flag;
+ cr_flag_damaged[i]= 1;
+ }
+ if(pr_mat_damaged[curf]) {
+ cr_lvl->faces[i].mat_nr= cr_lvl->next->faces[curf].mat_nr;
+ cr_mat_damaged[i]= 1;
+ }
+ }
+ }
+
+ cr_lvl= cr_lvl->prev;
+ }
+
+ /* Clear to original damages */
+ if(cr_flag_damaged) MEM_freeN(cr_flag_damaged);
+ if(cr_mat_damaged) MEM_freeN(cr_mat_damaged);
+ cr_flag_damaged= or_flag_damaged;
+ cr_mat_damaged= or_mat_damaged;
+
+ /* Update higher levels */
+ pr_lvl= current_level(me->mr);
+ cr_lvl= pr_lvl->next;
+ while(cr_lvl) {
+ swap= pr_flag_damaged;
+ pr_flag_damaged= cr_flag_damaged;
+ cr_flag_damaged= swap;
+
+ swap= pr_mat_damaged;
+ pr_mat_damaged= cr_mat_damaged;
+ cr_mat_damaged= swap;
+
+ /* Update faces */
+ for(i=0, curf= 0; i<pr_lvl->totface; ++i) {
+ const int sides= cr_lvl->prev->faces[i].v[3] ? 4 : 3;
+ for(j=0; j<sides; ++j, ++curf) {
+ if(pr_flag_damaged[i]) {
+ cr_lvl->faces[curf].flag= pr_lvl->faces[i].flag;
+ cr_flag_damaged[curf]= 1;
+ }
+ if(pr_mat_damaged[i]) {
+ cr_lvl->faces[curf].mat_nr= pr_lvl->faces[i].mat_nr;
+ cr_mat_damaged[curf]= 1;
+ }
+ }
+ }
+
+ pr_lvl= pr_lvl->next;
+ cr_lvl= cr_lvl->next;
+ }
+
+ if(pr_flag_damaged) MEM_freeN(pr_flag_damaged);
+ if(cr_flag_damaged) MEM_freeN(cr_flag_damaged);
+ if(pr_mat_damaged) MEM_freeN(pr_mat_damaged);
+ if(cr_mat_damaged) MEM_freeN(cr_mat_damaged);
+}
+
+void multires_update_colors(Mesh *me)
+{
+ MultiresLevel *lvl= BLI_findlink(&me->mr->levels,me->mr->current-1);
+ MultiresCol *pr_deltas= NULL, *cr_deltas= NULL;
+ EditMesh *em= G.obedit ? G.editMesh : NULL;
+ CustomData *src= em ? &em->fdata : &me->fdata;
+ EditFace *efa= NULL;
+ unsigned i,j,curf= 0;
+
+ if(me->mr->use_col) {
+ /* Calc initial deltas */
+ cr_deltas= MEM_callocN(sizeof(MultiresCol)*lvl->totface*4,"initial color/uv deltas");
+
+ if(em) efa= em->faces.first;
+ for(i=0; i<lvl->totface; ++i) {
+ MCol *col= em ? CustomData_em_get(src, efa->data, CD_MCOL) : &me->mcol[i*4];
+ for(j=0; j<4; ++j) {
+ if(me->mr->use_col) {
+ cr_deltas[i*4+j].a= col[j].a - lvl->colfaces[i].col[j].a;
+ cr_deltas[i*4+j].r= col[j].r - lvl->colfaces[i].col[j].r;
+ cr_deltas[i*4+j].g= col[j].g - lvl->colfaces[i].col[j].g;
+ cr_deltas[i*4+j].b= col[j].b - lvl->colfaces[i].col[j].b;
+ }
+ }
+ if(em) efa= efa->next;
+ }
+
+ /* Update current level */
+ if(em) efa= em->faces.first;
+ for(i=0; i<lvl->totface; ++i) {
+ MultiresColFace *f= &lvl->colfaces[i];
+
+ if(me->mr->use_col)
+ mcol_to_multires(f, em ? CustomData_em_get(src, efa->data, CD_MCOL) : &me->mcol[i*4]);
+
+ if(em) efa= efa->next;
+ }
+
+ /* Update higher levels */
+ lvl= lvl->next;
+ while(lvl) {
+ /* Set up new deltas, but keep the ones from the previous level */
+ if(pr_deltas) MEM_freeN(pr_deltas);
+ pr_deltas= cr_deltas;
+ cr_deltas= MEM_callocN(sizeof(MultiresCol)*lvl->totface*4,"color deltas");
+
+ curf= 0;
+ for(i=0; i<lvl->prev->totface; ++i) {
+ const char sides= lvl->prev->faces[i].v[3]?4:3;
+ MultiresCol cntr;
+
+ /* Find average color of 4 (or 3 for triangle) verts */
+ multires_col_avg(&cntr,&pr_deltas[i*4],sides);
+
+ for(j=0; j<sides; ++j) {
+ multires_col_avg2(&cr_deltas[curf*4],
+ &pr_deltas[i*4+j],
+ &pr_deltas[i*4+(j==0?sides-1:j-1)]);
+ cr_deltas[curf*4+1]= pr_deltas[i*4+j];
+ multires_col_avg2(&cr_deltas[curf*4+2],
+ &pr_deltas[i*4+j],
+ &pr_deltas[i*4+(j==sides-1?0:j+1)]);
+ cr_deltas[curf*4+3]= cntr;
+ ++curf;
+ }
+ }
+
+ for(i=0; i<lvl->totface; ++i) {
+ for(j=0; j<4; ++j) {
+ lvl->colfaces[i].col[j].a+= cr_deltas[i*4+j].a;
+ lvl->colfaces[i].col[j].r+= cr_deltas[i*4+j].r;
+ lvl->colfaces[i].col[j].g+= cr_deltas[i*4+j].g;
+ lvl->colfaces[i].col[j].b+= cr_deltas[i*4+j].b;
+ }
+ }
+
+ lvl= lvl->next;
+ }
+ if(pr_deltas) MEM_freeN(pr_deltas);
+ if(cr_deltas) MEM_freeN(cr_deltas);
+
+ /* Update lower levels */
+ lvl= me->mr->levels.last;
+ lvl= lvl->prev;
+ while(lvl) {
+ MultiresColFace *nf= lvl->next->colfaces;
+ for(i=0; i<lvl->totface; ++i) {
+ MultiresFace *f= &lvl->faces[i];
+ for(j=0; j<(f->v[3]?4:3); ++j) {
+ lvl->colfaces[i].col[j]= nf->col[1];
+ ++nf;
+ }
+ }
+ lvl= lvl->prev;
+ }
+ }
+}
+
+void multires_update_levels(Mesh *me, const int render)
+{
+ EditMesh *em= (!render && G.obedit) ? G.editMesh : NULL;
+
+ multires_update_first_level(me, em);
+ multires_update_vertices(me, em);
+ multires_update_faces(me, em);
+ multires_update_colors(me);
+}
+
+static void check_colors(Mesh *me)
+{
+ CustomData *src= G.obedit ? &G.editMesh->fdata : &me->fdata;
+ const char col= CustomData_has_layer(src, CD_MCOL);
+
+ /* Check if vertex colors have been deleted or added */
+ if(me->mr->use_col && !col)
+ me->mr->use_col= 0;
+ else if(!me->mr->use_col && col) {
+ me->mr->use_col= 1;
+ multires_load_cols(me);
+ }
+}
+
+static unsigned int find_mid_edge(ListBase *vert_edge_map,
+ MultiresLevel *lvl,
+ const unsigned int v1,
+ const unsigned int v2 )
+{
+ MultiresMapNode *n= vert_edge_map[v1].first;
+ while(n) {
+ if(lvl->edges[n->Index].v[0]==v2 ||
+ lvl->edges[n->Index].v[1]==v2)
+ return lvl->edges[n->Index].mid;
+
+ n= n->next;
+ }
+ return -1;
+}
+
+static void medge_flag_to_eed(const short flag, const char crease, EditEdge *eed)
+{
+ if(!eed) return;
+
+ if(flag & ME_SEAM) eed->seam= 1;
+ if(flag & ME_SHARP) eed->sharp = 1;
+ if(flag & SELECT) eed->f |= SELECT;
+ if(flag & ME_FGON) eed->h= EM_FGON;
+ if(flag & ME_HIDE) eed->h |= 1;
+
+ eed->crease= ((float)crease)/255.0;
+}
+
+static float clamp_component(const float c)
+{
+ if(c<0) return 0;
+ else if(c>255) return 255;
+ else return c;
+}
+
+static void multires_to_mcol(MultiresColFace *f, MCol mcol[4])
+{
+ unsigned char j;
+ for(j=0; j<4; ++j) {
+ mcol->a= clamp_component(f->col[j].a);
+ mcol->r= clamp_component(f->col[j].r);
+ mcol->g= clamp_component(f->col[j].g);
+ mcol->b= clamp_component(f->col[j].b);
+ ++mcol;
+ }
+}
+
+void multires_level_to_mesh(Object *ob, Mesh *me, const int render)
+{
+ MultiresLevel *lvl= BLI_findlink(&me->mr->levels,me->mr->current-1);
+ int i;
+ EditMesh *em= (!render && G.obedit) ? G.editMesh : NULL;
+ EditVert **eves= NULL;
+ EditEdge *eed= NULL;
+
+ if(em) {
+ /* Remove editmesh elements */
+ free_editMesh(em);
+
+ eves= MEM_callocN(sizeof(EditVert*)*lvl->totvert, "editvert pointers");
+ } else {
+ CustomData_free_layer_active(&me->vdata, CD_MVERT, me->totvert);
+ CustomData_free_layer_active(&me->edata, CD_MEDGE, me->totedge);
+ CustomData_free_layer_active(&me->fdata, CD_MFACE, me->totface);
+ CustomData_free_layer_active(&me->vdata, CD_MDEFORMVERT, me->totvert);
+ CustomData_free_layers(&me->fdata, CD_MTFACE, me->totface);
+ CustomData_free_layers(&me->fdata, CD_MCOL, me->totface);
+
+ me->totvert= lvl->totvert;
+ me->totface= lvl->totface;
+ me->totedge= lvl->totedge;
+
+ CustomData_add_layer(&me->vdata, CD_MVERT, CD_CALLOC, NULL, me->totvert);
+ CustomData_add_layer(&me->edata, CD_MEDGE, CD_CALLOC, NULL, me->totedge);
+ CustomData_add_layer(&me->fdata, CD_MFACE, CD_CALLOC, NULL, me->totface);
+ mesh_update_customdata_pointers(me);
+ }
+
+ /* Vertices/Edges/Faces */
+
+ for(i=0; i<lvl->totvert; ++i) {
+ if(em) {
+ eves[i]= addvertlist(me->mr->verts[i].co, NULL);
+ if(me->mr->verts[i].flag & 1) eves[i]->f |= SELECT;
+ if(me->mr->verts[i].flag & ME_HIDE) eves[i]->h= 1;
+ eves[i]->data= NULL;
+ }
+ else
+ me->mvert[i]= me->mr->verts[i];
+ }
+ for(i=0; i<lvl->totedge; ++i) {
+ if(em) {
+ addedgelist(eves[lvl->edges[i].v[0]], eves[lvl->edges[i].v[1]], NULL);
+ } else {
+ me->medge[i].v1= lvl->edges[i].v[0];
+ me->medge[i].v2= lvl->edges[i].v[1];
+ me->medge[i].flag &= ~ME_HIDE;
+ }
+ }
+ for(i=0; i<lvl->totface; ++i) {
+ if(em) {
+ EditVert *eve4= lvl->faces[i].v[3] ? eves[lvl->faces[i].v[3]] : NULL;
+ EditFace *efa= addfacelist(eves[lvl->faces[i].v[0]], eves[lvl->faces[i].v[1]],
+ eves[lvl->faces[i].v[2]], eve4, NULL, NULL);
+ efa->flag= lvl->faces[i].flag & ~ME_HIDE;
+ efa->mat_nr= lvl->faces[i].mat_nr;
+ if(lvl->faces[i].flag & ME_FACE_SEL)
+ efa->f |= SELECT;
+ if(lvl->faces[i].flag & ME_HIDE) efa->h= 1;
+ efa->data= NULL;
+ }
+ else {
+ me->mface[i].v1= lvl->faces[i].v[0];
+ me->mface[i].v2= lvl->faces[i].v[1];
+ me->mface[i].v3= lvl->faces[i].v[2];
+ me->mface[i].v4= lvl->faces[i].v[3];
+ me->mface[i].flag= lvl->faces[i].flag;
+ me->mface[i].flag &= ~ME_HIDE;
+ me->mface[i].mat_nr= lvl->faces[i].mat_nr;
+ }
+ }
+
+ /* Edge flags */
+ if(em) eed= em->edges.first;
+ if(lvl==me->mr->levels.first) {
+ for(i=0; i<lvl->totedge; ++i) {
+ if(em) {
+ medge_flag_to_eed(me->mr->edge_flags[i], me->mr->edge_creases[i], eed);
+ eed= eed->next;
+ }
+ else {
+ me->medge[i].flag= me->mr->edge_flags[i];
+ me->medge[i].crease= me->mr->edge_creases[i];
+ }
+ }
+ } else {
+ MultiresLevel *lvl1= me->mr->levels.first;
+ const int last= lvl1->totedge * pow(2, me->mr->current-1);
+ for(i=0; i<last; ++i) {
+ const int ndx= i / pow(2, me->mr->current-1);
+
+ if(em) {
+ medge_flag_to_eed(me->mr->edge_flags[ndx], me->mr->edge_creases[ndx], eed);
+ eed= eed->next;
+ }
+ else {
+ me->medge[i].flag= me->mr->edge_flags[ndx];
+ me->medge[i].crease= me->mr->edge_creases[ndx];
+ }
+ }
+ }
+
+ if(em) {
+ eed= em->edges.first;
+ for(i=0, eed= em->edges.first; i<lvl->totedge; ++i, eed= eed->next) {
+ eed->h= me->mr->verts[lvl->edges[i].v[0]].flag & ME_HIDE ||
+ me->mr->verts[lvl->edges[i].v[1]].flag & ME_HIDE;
+ }
+ }
+
+ EM_select_flush();
+
+ multires_customdata_to_mesh(me, em, lvl, &me->mr->vdata, em ? &em->vdata : &me->vdata, CD_MDEFORMVERT);
+ multires_customdata_to_mesh(me, em, lvl, &me->mr->fdata, em ? &em->fdata : &me->fdata, CD_MTFACE);
+
+ /* Colors */
+ if(me->mr->use_col) {
+ MCol c[4];
+ EditFace *efa= NULL;
+ CustomData *src= em ? &em->fdata : &me->fdata;
+ if(em) {
+ if(me->mr->use_col) EM_add_data_layer(src, CD_MCOL);
+ efa= em->faces.first;
+ }
+ else {
+ if(me->mr->use_col) me->mcol= CustomData_add_layer(src, CD_MCOL, CD_CALLOC, NULL, me->totface);
+ }
+
+ for(i=0; i<lvl->totface; ++i) {
+ if(em) {
+ if(me->mr->use_col) {
+ multires_to_mcol(&lvl->colfaces[i], c);
+ CustomData_em_set(src, efa->data, CD_MCOL, c);
+ }
+ efa= efa->next;
+ }
+ else if(me->mr->use_col) multires_to_mcol(&lvl->colfaces[i], &me->mcol[i*4]);
+ }
+
+ }
+
+ mesh_update_customdata_pointers(me);
+
+ if(em) {
+ MEM_freeN(eves);
+ DAG_object_flush_update(G.scene, ob, OB_RECALC_DATA);
+ recalc_editnormals();
+ } else {
+ multires_edge_level_update(ob,me);
+ DAG_object_flush_update(G.scene, ob, OB_RECALC_DATA);
+ mesh_calc_normals(me->mvert, me->totvert, me->mface, me->totface, NULL);
+ }
+}
+
+void multires_add_level(Object *ob, Mesh *me, const char subdiv_type)
+{
+ int i,j, curf, cure;
+ MultiresLevel *lvl= NULL;
+ MultiApplyData data;
+ MVert *oldverts= NULL;
+
+ lvl= MEM_callocN(sizeof(MultiresLevel), "multireslevel");
+ if(me->pv) sculptmode_pmv_off(me);
+
+ check_colors(me);
+ multires_update_levels(me, 0);
+
+ ++me->mr->level_count;
+ BLI_addtail(&me->mr->levels,lvl);
+
+ /* Create vertices
+ =============== */
+ lvl->totvert= lvl->prev->totvert + lvl->prev->totedge + lvl->prev->totface;
+ oldverts= me->mr->verts;
+ me->mr->verts= MEM_callocN(sizeof(MVert)*lvl->totvert, "multitres verts");
+ /* Copy old verts */
+ for(i=0; i<lvl->prev->totvert; ++i)
+ me->mr->verts[i]= oldverts[i];
+ /* Create new edge verts */
+ for(i=0; i<lvl->prev->totedge; ++i) {
+ VecMidf(me->mr->verts[lvl->prev->totvert + i].co,
+ oldverts[lvl->prev->edges[i].v[0]].co,
+ oldverts[lvl->prev->edges[i].v[1]].co);
+ lvl->prev->edges[i].mid= lvl->prev->totvert + i;
+ }
+ /* Create new face verts */
+ for(i=0; i<lvl->prev->totface; ++i) {
+ lvl->prev->faces[i].mid= lvl->prev->totvert + lvl->prev->totedge + i;
+ }
+
+ multires_calc_temp_data(lvl->prev);
+
+ /* Create faces
+ ============ */
+ /* Allocate all the new faces (each triangle creates three, and
+ each quad creates four */
+ lvl->totface= 0;
+ for(i=0; i<lvl->prev->totface; ++i)
+ lvl->totface+= lvl->prev->faces[i].v[3] ? 4 : 3;
+ lvl->faces= MEM_callocN(sizeof(MultiresFace)*lvl->totface,"multires faces");
+
+ curf= 0;
+ for(i=0; i<lvl->prev->totface; ++i) {
+ const int max= lvl->prev->faces[i].v[3] ? 3 : 2;
+
+ for(j=0; j<max+1; ++j) {
+ lvl->faces[curf].v[0]= find_mid_edge(lvl->prev->vert_edge_map,lvl->prev,
+ lvl->prev->faces[i].v[j],
+ lvl->prev->faces[i].v[j==0?max:j-1]);
+ lvl->faces[curf].v[1]= lvl->prev->faces[i].v[j];
+ lvl->faces[curf].v[2]= find_mid_edge(lvl->prev->vert_edge_map,lvl->prev,
+ lvl->prev->faces[i].v[j],
+ lvl->prev->faces[i].v[j==max?0:j+1]);
+ lvl->faces[curf].v[3]= lvl->prev->totvert + lvl->prev->totedge + i;
+ lvl->faces[curf].flag= lvl->prev->faces[i].flag;
+ lvl->faces[curf].mat_nr= lvl->prev->faces[i].mat_nr;
+
+ ++curf;
+ }
+ }
+
+ /* Create edges
+ ============ */
+ /* Figure out how many edges to allocate */
+ lvl->totedge= lvl->prev->totedge*2;
+ for(i=0; i<lvl->prev->totface; ++i)
+ lvl->totedge+= lvl->prev->faces[i].v[3]?4:3;
+ lvl->edges= MEM_callocN(sizeof(MultiresEdge)*lvl->totedge,"multires edges");
+
+ for(i=0; i<lvl->prev->totedge; ++i) {
+ lvl->edges[i*2].v[0]= lvl->prev->edges[i].v[0];
+ lvl->edges[i*2].v[1]= lvl->prev->edges[i].mid;
+ lvl->edges[i*2+1].v[0]= lvl->prev->edges[i].mid;
+ lvl->edges[i*2+1].v[1]= lvl->prev->edges[i].v[1];
+ }
+ /* Add edges inside of old polygons */
+ curf= 0;
+ cure= lvl->prev->totedge*2;
+ for(i=0; i<lvl->prev->totface; ++i) {
+ for(j=0; j<(lvl->prev->faces[i].v[3]?4:3); ++j) {
+ lvl->edges[cure].v[0]= lvl->faces[curf].v[2];
+ lvl->edges[cure].v[1]= lvl->faces[curf].v[3];
+ ++cure;
+ ++curf;
+ }
+ }
+
+ /* Smooth vertices
+ =============== */
+ for(i=0; i<lvl->prev->totface; ++i) {
+ const MultiresFace *f= &lvl->prev->faces[i];
+ data.corner1= oldverts[f->v[0]].co;
+ data.corner2= oldverts[f->v[1]].co;
+ data.corner3= oldverts[f->v[2]].co;
+ data.corner4= oldverts[f->v[3]].co;
+ data.quad= f->v[3] ? 1 : 0;
+ multi_apply(me->mr->verts[f->mid].co, &data, 3, catmullclark_smooth_face);
+ }
+
+ if(subdiv_type == 0) {
+ for(i=0; i<lvl->prev->totedge; ++i) {
+ const MultiresEdge *e= &lvl->prev->edges[i];
+ data.boundary= lvl->prev->edge_boundary_states[i];
+ edge_face_neighbor_midpoints_accum(&data,lvl->prev, me->mr->verts, sizeof(MVert),e);
+ data.endpoint1= oldverts[e->v[0]].co;
+ data.endpoint2= oldverts[e->v[1]].co;
+ multi_apply(me->mr->verts[e->mid].co, &data, 3, catmullclark_smooth_edge);
+ }
+
+ for(i=0; i<lvl->prev->totvert; ++i) {
+ data.boundary= multires_vert_is_boundary(lvl->prev,i);
+ data.original= oldverts[i].co;
+ data.edge_count= BLI_countlist(&lvl->prev->vert_edge_map[i]);
+ if(data.boundary)
+ boundary_edges_average(&data,lvl->prev, oldverts, sizeof(MVert),i);
+ else {
+ vert_face_neighbor_midpoints_average(&data,lvl->prev, me->mr->verts,
+ sizeof(MVert),i);
+ vert_edge_neighbor_midpoints_average(&data,lvl->prev, oldverts,
+ sizeof(MVert),i);
+ }
+ multi_apply(me->mr->verts[i].co, &data, 3, catmullclark_smooth_vert);
+ }
+ }
+
+ multires_free_temp_data(lvl->prev);
+ MEM_freeN(oldverts);
+
+ /* Vertex Colors
+ ============= */
+ curf= 0;
+ if(me->mr->use_col) {
+ MultiresColFace *cf= MEM_callocN(sizeof(MultiresColFace)*lvl->totface,"Multirescolfaces");
+ lvl->colfaces= cf;
+ for(i=0; i<lvl->prev->totface; ++i) {
+ const char sides= lvl->prev->faces[i].v[3]?4:3;
+ MultiresCol cntr;
+
+ /* Find average color of 4 (or 3 for triangle) verts */
+ multires_col_avg(&cntr,lvl->prev->colfaces[i].col,sides);
+
+ for(j=0; j<sides; ++j) {
+ multires_col_avg2(&cf->col[0],
+ &lvl->prev->colfaces[i].col[j],
+ &lvl->prev->colfaces[i].col[j==0?sides-1:j-1]);
+ cf->col[1]= lvl->prev->colfaces[i].col[j];
+ multires_col_avg2(&cf->col[2],
+ &lvl->prev->colfaces[i].col[j],
+ &lvl->prev->colfaces[i].col[j==sides-1?0:j+1]);
+ cf->col[3]= cntr;
+
+ ++cf;
+ }
+ }
+ }
+
+ me->mr->newlvl= me->mr->level_count;
+ me->mr->current= me->mr->newlvl;
+ /* Unless the render level has been set to something other than the
+ highest level (by the user), increment the render level to match
+ the highest available level */
+ if(me->mr->renderlvl == me->mr->level_count - 1) me->mr->renderlvl= me->mr->level_count;
+
+ multires_level_to_mesh(ob, me, 0);
+}
+
+void multires_set_level(Object *ob, Mesh *me, const int render)
+{
+ if(me->pv) sculptmode_pmv_off(me);
+
+ check_colors(me);
+ multires_update_levels(me, render);
+
+ me->mr->current= me->mr->newlvl;
+ if(me->mr->current<1) me->mr->current= 1;
+ else if(me->mr->current>me->mr->level_count) me->mr->current= me->mr->level_count;
+
+ multires_level_to_mesh(ob, me, render);
+}
+
+/* Update the edge visibility flags to only show edges on or below the edgelvl */
+void multires_edge_level_update(Object *ob, Mesh *me)
+{
+ if(!G.obedit) {
+ MultiresLevel *cr_lvl= BLI_findlink(&me->mr->levels,me->mr->current-1);
+ MultiresLevel *edge_lvl= BLI_findlink(&me->mr->levels,me->mr->edgelvl-1);
+ const int threshold= edge_lvl->totedge * powf(2, me->mr->current - me->mr->edgelvl);
+ unsigned i;
+
+ for(i=0; i<cr_lvl->totedge; ++i) {
+ const int ndx= me->pv ? me->pv->edge_map[i] : i;
+ if(ndx != -1) { /* -1= hidden edge */
+ if(me->mr->edgelvl >= me->mr->current || i<threshold)
+ me->medge[ndx].flag |= ME_EDGEDRAW | ME_EDGERENDER;
+ else
+ me->medge[ndx].flag &= ~ME_EDGEDRAW & ~ME_EDGERENDER;
+ }
+ }
+
+ DAG_object_flush_update(G.scene, ob, OB_RECALC_DATA);
+ }
+}