More F-Modifier Tweaks:
[blender.git] / source / blender / blenkernel / intern / CCGSubSurf.c
index bec741c64e6dee206b1b1bc27401880bb1dc91fb..eb316c64af488f450b8ea8a8bc2c3cc9265498c2 100644 (file)
@@ -3,10 +3,22 @@
 #include <stdlib.h>
 #include <string.h>
 #include <math.h>
-#include <float.h>
 
 #include "CCGSubSurf.h"
 
+#include "MEM_guardedalloc.h"
+#include "BLO_sys_types.h" // for intptr_t support
+
+#ifdef _MSC_VER
+#define CCG_INLINE __inline
+#else
+#define CCG_INLINE inline
+#endif
+
+/* used for normalize_v3 in BLI_math_vector
+ * float.h's FLT_EPSILON causes trouble with subsurf normals - campbell */
+#define EPSILON (1.0e-35f)
+
 /***/
 
 typedef unsigned char  byte;
@@ -35,7 +47,7 @@ typedef struct _EHash {
 #define EHASH_alloc(eh, nb)                    ((eh)->allocatorIFC.alloc((eh)->allocator, nb))
 #define EHASH_free(eh, ptr)                    ((eh)->allocatorIFC.free((eh)->allocator, ptr))
 
-#define EHASH_hash(eh, item)   (((unsigned long) (item))%((unsigned int) (eh)->curSize))
+#define EHASH_hash(eh, item)   (((uintptr_t) (item))%((unsigned int) (eh)->curSize))
 
 static EHash *_ehash_new(int estimatedNumEntries, CCGAllocatorIFC *allocatorIFC, CCGAllocatorHDL allocator) {
        EHash *eh = allocatorIFC->alloc(allocator, sizeof(*eh));
@@ -199,21 +211,21 @@ static CCGAllocatorIFC *_getStandardAllocatorIFC(void) {
 static int VertDataEqual(float *a, float *b) {
        return a[0]==b[0] && a[1]==b[1] && a[2]==b[2];
 }
-#define VertDataZero(av)                               { float *a = (float*) av; a[0] = a[1] = a[2] = 0.0f; }
-#define VertDataCopy(av, bv)                   { float *a = (float*) av, *b = (float*) bv; a[0] =b[0]; a[1] =b[1]; a[2] =b[2]; }
-#define VertDataAdd(av, bv)                            { float *a = (float*) av, *b = (float*) bv; a[0]+=b[0]; a[1]+=b[1]; a[2]+=b[2]; }
-#define VertDataSub(av, bv)                            { float *a = (float*) av, *b = (float*) bv; a[0]-=b[0]; a[1]-=b[1]; a[2]-=b[2]; }
-#define VertDataMulN(av, n)                            { float *a = (float*) av; a[0]*=n; a[1]*=n; a[2]*=n; }
+#define VertDataZero(av)                               { float *_a = (float*) av; _a[0] = _a[1] = _a[2] = 0.0f; }
+#define VertDataCopy(av, bv)                   { float *_a = (float*) av, *_b = (float*) bv; _a[0] =_b[0]; _a[1] =_b[1]; _a[2] =_b[2]; }
+#define VertDataAdd(av, bv)                            { float *_a = (float*) av, *_b = (float*) bv; _a[0]+=_b[0]; _a[1]+=_b[1]; _a[2]+=_b[2]; }
+#define VertDataSub(av, bv)                            { float *_a = (float*) av, *_b = (float*) bv; _a[0]-=_b[0]; _a[1]-=_b[1]; _a[2]-=_b[2]; }
+#define VertDataMulN(av, n)                            { float *_a = (float*) av; _a[0]*=n; _a[1]*=n; _a[2]*=n; }
 #define VertDataAvg4(tv, av, bv, cv, dv) \
        { \
-               float *t = (float*) tv, *a = (float*) av, *b = (float*) bv, *c = (float*) cv, *d = (float*) dv; \
-               t[0] = (a[0]+b[0]+c[0]+d[0])*.25; \
-               t[1] = (a[1]+b[1]+c[1]+d[1])*.25; \
-               t[2] = (a[2]+b[2]+c[2]+d[2])*.25; \
+               float *_t = (float*) tv, *_a = (float*) av, *_b = (float*) bv, *_c = (float*) cv, *_d = (float*) dv; \
+               _t[0] = (_a[0]+_b[0]+_c[0]+_d[0])*.25; \
+               _t[1] = (_a[1]+_b[1]+_c[1]+_d[1])*.25; \
+               _t[2] = (_a[2]+_b[2]+_c[2]+_d[2])*.25; \
        }
-#define NormZero(av)                                   { float *a = (float*) av; a[0] = a[1] = a[2] = 0.0f; }
-#define NormCopy(av, bv)                               { float *a = (float*) av, *b = (float*) bv; a[0] =b[0]; a[1] =b[1]; a[2] =b[2]; }
-#define NormAdd(av, bv)                                        { float *a = (float*) av, *b = (float*) bv; a[0]+=b[0]; a[1]+=b[1]; a[2]+=b[2]; }
+#define NormZero(av)                                   { float *_a = (float*) av; _a[0] = _a[1] = _a[2] = 0.0f; }
+#define NormCopy(av, bv)                               { float *_a = (float*) av, *_b = (float*) bv; _a[0] =_b[0]; _a[1] =_b[1]; _a[2] =_b[2]; }
+#define NormAdd(av, bv)                                        { float *_a = (float*) av, *_b = (float*) bv; _a[0]+=_b[0]; _a[1]+=_b[1]; _a[2]+=_b[2]; }
 
 
 static int _edge_isBoundary(CCGEdge *e);
@@ -223,6 +235,7 @@ static int _edge_isBoundary(CCGEdge *e);
 enum {
        Vert_eEffected=         (1<<0),
        Vert_eChanged=          (1<<1),
+       Vert_eSeam=                     (1<<2),
 } VertFlags;
 enum {
        Edge_eEffected=         (1<<0),
@@ -297,6 +310,7 @@ struct _CCGSubSurf {
        int numGrids;
        int allowEdgeCreation;
        float defaultCreaseValue;
+       void *defaultEdgeUserData;
 
        void *q, *r;
                
@@ -326,7 +340,7 @@ struct _CCGSubSurf {
 
 /***/
 
-static CCGVert *_vert_new(CCGVertHDL vHDL, int levels, int dataSize, CCGSubSurf *ss) {
+static CCGVert *_vert_new(CCGVertHDL vHDL, CCGSubSurf *ss) {
        CCGVert *v = CCGSUBSURF_alloc(ss, sizeof(CCGVert) + ss->meshIFC.vertDataSize * (ss->subdivLevels+1) + ss->meshIFC.vertUserSize);
        byte *userData;
 
@@ -342,7 +356,7 @@ static CCGVert *_vert_new(CCGVertHDL vHDL, int levels, int dataSize, CCGSubSurf
 
        return v;
 }
-static void _vert_remEdge(CCGVert *v, CCGEdge *e, CCGSubSurf *ss) {
+static void _vert_remEdge(CCGVert *v, CCGEdge *e) {
        int i;
        for (i=0; i<v->numEdges; i++) {
                if (v->edges[i]==e) {
@@ -351,7 +365,7 @@ static void _vert_remEdge(CCGVert *v, CCGEdge *e, CCGSubSurf *ss) {
                }
        }
 }
-static void _vert_remFace(CCGVert *v, CCGFace *f, CCGSubSurf *ss) {
+static void _vert_remFace(CCGVert *v, CCGFace *f) {
        int i;
        for (i=0; i<v->numFaces; i++) {
                if (v->faces[i]==f) {
@@ -399,9 +413,13 @@ static void _vert_free(CCGVert *v, CCGSubSurf *ss) {
        CCGSUBSURF_free(ss, v);
 }
 
+static int VERT_seam(CCGVert *v) {
+       return ((v->flags & Vert_eSeam) != 0);
+}
+
 /***/
 
-static CCGEdge *_edge_new(CCGEdgeHDL eHDL, CCGVert *v0, CCGVert *v1, float crease, int levels, int dataSize, CCGSubSurf *ss) {
+static CCGEdge *_edge_new(CCGEdgeHDL eHDL, CCGVert *v0, CCGVert *v1, float crease, CCGSubSurf *ss) {
        CCGEdge *e = CCGSUBSURF_alloc(ss, sizeof(CCGEdge) + ss->meshIFC.vertDataSize *((ss->subdivLevels+1) + (1<<(ss->subdivLevels+1))-1) + ss->meshIFC.edgeUserSize);
        byte *userData;
 
@@ -421,7 +439,7 @@ static CCGEdge *_edge_new(CCGEdgeHDL eHDL, CCGVert *v0, CCGVert *v1, float creas
 
        return e;
 }
-static void _edge_remFace(CCGEdge *e, CCGFace *f, CCGSubSurf *ss) {
+static void _edge_remFace(CCGEdge *e, CCGFace *f) {
        int i;
        for (i=0; i<e->numFaces; i++) {
                if (e->faces[i]==f) {
@@ -454,7 +472,6 @@ static float *_edge_getNo(CCGEdge *e, int lvl, int x, int dataSize, int normalDa
        int levelBase = lvl + (1<<lvl) - 1;
        return (float*) &EDGE_getLevelData(e)[dataSize*(levelBase + x) + normalDataOffset];
 }
-
 static void *_edge_getCoVert(CCGEdge *e, CCGVert *v, int lvl, int x, int dataSize) {
        int levelBase = lvl + (1<<lvl) - 1;
        if (v==e->v0) {
@@ -469,28 +486,25 @@ static void _edge_free(CCGEdge *e, CCGSubSurf *ss) {
        CCGSUBSURF_free(ss, e);
 }
 static void _edge_unlinkMarkAndFree(CCGEdge *e, CCGSubSurf *ss) {
-       _vert_remEdge(e->v0, e, ss);
-       _vert_remEdge(e->v1, e, ss);
+       _vert_remEdge(e->v0, e);
+       _vert_remEdge(e->v1, e);
        e->v0->flags |= Vert_eEffected;
        e->v1->flags |= Vert_eEffected;
        _edge_free(e, ss);
 }
 
-static float EDGE_getSharpness(CCGEdge *e, int lvl, CCGSubSurf *ss) {
-       float sharpness = e->crease;
-       while (lvl--) {
-               if (sharpness>1.0) {
-                       sharpness -= 1.0;
-               } else {
-                       sharpness = 0.0;
-               }
-       }
-       return sharpness;
+static float EDGE_getSharpness(CCGEdge *e, int lvl) {
+       if (!lvl)
+               return e->crease;
+       else if (!e->crease)
+               return 0.0;
+       else if (e->crease - lvl < 0.0)
+               return 0.0;
+       else
+               return e->crease - lvl;
 }
 
-/***/
-
-static CCGFace *_face_new(CCGFaceHDL fHDL, CCGVert **verts, CCGEdge **edges, int numVerts, int levels, int dataSize, CCGSubSurf *ss) {
+static CCGFace *_face_new(CCGFaceHDL fHDL, CCGVert **verts, CCGEdge **edges, int numVerts, CCGSubSurf *ss) {
        int maxGridSize = 1 + (1<<(ss->subdivLevels-1));
        CCGFace *f = CCGSUBSURF_alloc(ss, sizeof(CCGFace) + sizeof(CCGVert*)*numVerts + sizeof(CCGEdge*)*numVerts + ss->meshIFC.vertDataSize *(1 + numVerts*maxGridSize + numVerts*maxGridSize*maxGridSize) + ss->meshIFC.faceUserSize);
        byte *userData;
@@ -514,19 +528,25 @@ static CCGFace *_face_new(CCGFaceHDL fHDL, CCGVert **verts, CCGEdge **edges, int
        return f;
 }
 
-static void *_face_getIECo(CCGFace *f, int lvl, int S, int x, int levels, int dataSize) {
+static CCG_INLINE void *_face_getIECo(CCGFace *f, int lvl, int S, int x, int levels, int dataSize) {
        int maxGridSize = 1 + (1<<(levels-1));
        int spacing = 1<<(levels-lvl);
        byte *gridBase = FACE_getCenterData(f) + dataSize*(1 + S*(maxGridSize + maxGridSize*maxGridSize));
        return &gridBase[dataSize*x*spacing];
 }
-static void *_face_getIFCo(CCGFace *f, int lvl, int S, int x, int y, int levels, int dataSize) {
+static CCG_INLINE void *_face_getIENo(CCGFace *f, int lvl, int S, int x, int levels, int dataSize, int normalDataOffset) {
+       int maxGridSize = 1 + (1<<(levels-1));
+       int spacing = 1<<(levels-lvl);
+       byte *gridBase = FACE_getCenterData(f) + dataSize*(1 + S*(maxGridSize + maxGridSize*maxGridSize));
+       return &gridBase[dataSize*x*spacing + normalDataOffset];
+}
+static CCG_INLINE void *_face_getIFCo(CCGFace *f, int lvl, int S, int x, int y, int levels, int dataSize) {
        int maxGridSize = 1 + (1<<(levels-1));
        int spacing = 1<<(levels-lvl);
        byte *gridBase = FACE_getCenterData(f) + dataSize*(1 + S*(maxGridSize + maxGridSize*maxGridSize));
        return &gridBase[dataSize*(maxGridSize + (y*maxGridSize + x)*spacing)];
 }
-static float *_face_getIFNo(CCGFace *f, int lvl, int S, int x, int y, int levels, int dataSize, int normalDataOffset) {
+static CCG_INLINE float *_face_getIFNo(CCGFace *f, int lvl, int S, int x, int y, int levels, int dataSize, int normalDataOffset) {
        int maxGridSize = 1 + (1<<(levels-1));
        int spacing = 1<<(levels-lvl);
        byte *gridBase = FACE_getCenterData(f) + dataSize*(1 + S*(maxGridSize + maxGridSize*maxGridSize));
@@ -539,7 +559,7 @@ static int _face_getVertIndex(CCGFace *f, CCGVert *v) {
                        return i;
        return -1;
 }
-static void *_face_getIFCoEdge(CCGFace *f, CCGEdge *e, int lvl, int eX, int eY, int levels, int dataSize) {
+static CCG_INLINE void *_face_getIFCoEdge(CCGFace *f, CCGEdge *e, int lvl, int eX, int eY, int levels, int dataSize) {
        int maxGridSize = 1 + (1<<(levels-1));
        int spacing = 1<<(levels-lvl);
        int S, x, y, cx, cy;
@@ -587,7 +607,7 @@ void _face_calcIFNo(CCGFace *f, int lvl, int S, int x, int y, float *no, int lev
 
        length = sqrt(no[0]*no[0] + no[1]*no[1] + no[2]*no[2]);
 
-       if (length>FLT_EPSILON) {
+       if (length>EPSILON) {
                float invLength = 1.f/length;
 
                no[0] *= invLength;
@@ -604,8 +624,8 @@ static void _face_free(CCGFace *f, CCGSubSurf *ss) {
 static void _face_unlinkMarkAndFree(CCGFace *f, CCGSubSurf *ss) {
        int j;
        for (j=0; j<f->numVerts; j++) {
-               _vert_remFace(FACE_getVerts(f)[j], f, ss);
-               _edge_remFace(FACE_getEdges(f)[j], f, ss);
+               _vert_remFace(FACE_getVerts(f)[j], f);
+               _edge_remFace(FACE_getEdges(f)[j], f);
                FACE_getVerts(f)[j]->flags |= Vert_eEffected;
        }
        _face_free(f, ss);
@@ -637,6 +657,7 @@ CCGSubSurf *ccgSubSurf_new(CCGMeshIFC *ifc, int subdivLevels, CCGAllocatorIFC *a
                ss->numGrids = 0;
                ss->allowEdgeCreation = 0;
                ss->defaultCreaseValue = 0;
+               ss->defaultEdgeUserData = NULL;
 
                ss->useAgeCounts = 0;
                ss->vertUserAgeOffset = ss->edgeUserAgeOffset = ss->faceUserAgeOffset = 0;
@@ -669,12 +690,13 @@ void ccgSubSurf_free(CCGSubSurf *ss) {
                _ehash_free(ss->oldEMap, (EHEntryFreeFP) _edge_free, ss);
                _ehash_free(ss->oldVMap, (EHEntryFreeFP) _vert_free, ss);
 
-               CCGSUBSURF_free(ss, ss->tempVerts);
-               CCGSUBSURF_free(ss, ss->tempEdges);
+               MEM_freeN(ss->tempVerts);
+               MEM_freeN(ss->tempEdges);
        }
 
        CCGSUBSURF_free(ss, ss->r);
        CCGSUBSURF_free(ss, ss->q);
+       if (ss->defaultEdgeUserData) CCGSUBSURF_free(ss, ss->defaultEdgeUserData);
 
        _ehash_free(ss->fMap, (EHEntryFreeFP) _face_free, ss);
        _ehash_free(ss->eMap, (EHEntryFreeFP) _edge_free, ss);
@@ -687,12 +709,30 @@ void ccgSubSurf_free(CCGSubSurf *ss) {
        }
 }
 
-CCGError ccgSubSurf_setAllowEdgeCreation(CCGSubSurf *ss, int allowEdgeCreation, float defaultCreaseValue) {
+CCGError ccgSubSurf_setAllowEdgeCreation(CCGSubSurf *ss, int allowEdgeCreation, float defaultCreaseValue, void *defaultUserData) {
+       if (ss->defaultEdgeUserData) {
+               CCGSUBSURF_free(ss, ss->defaultEdgeUserData);
+       }
+
        ss->allowEdgeCreation = !!allowEdgeCreation;
        ss->defaultCreaseValue = defaultCreaseValue;
+       ss->defaultEdgeUserData = CCGSUBSURF_alloc(ss, ss->meshIFC.edgeUserSize);
+
+       if (defaultUserData) {
+               memcpy(ss->defaultEdgeUserData, defaultUserData, ss->meshIFC.edgeUserSize);
+       } else {
+               memset(ss->defaultEdgeUserData, 0, ss->meshIFC.edgeUserSize);
+       }
 
        return eCCGError_None;
 }
+void ccgSubSurf_getAllowEdgeCreation(CCGSubSurf *ss, int *allowEdgeCreation_r, float *defaultCreaseValue_r, void *defaultUserData_r) {
+       if (allowEdgeCreation_r) *allowEdgeCreation_r = ss->allowEdgeCreation;
+       if (ss->allowEdgeCreation) {
+               if (defaultCreaseValue_r) *defaultCreaseValue_r = ss->defaultCreaseValue;
+               if (defaultUserData_r) memcpy(defaultUserData_r, ss->defaultEdgeUserData, ss->meshIFC.edgeUserSize);
+       }
+}
 
 CCGError ccgSubSurf_setSubdivisionLevels(CCGSubSurf *ss, int subdivisionLevels) {
        if (subdivisionLevels<=0) {
@@ -776,8 +816,8 @@ CCGError ccgSubSurf_initFullSync(CCGSubSurf *ss) {
        ss->numGrids = 0;
 
        ss->lenTempArrays = 12;
-       ss->tempVerts = CCGSUBSURF_alloc(ss, sizeof(*ss->tempVerts)*ss->lenTempArrays);
-       ss->tempEdges = CCGSUBSURF_alloc(ss, sizeof(*ss->tempEdges)*ss->lenTempArrays);
+       ss->tempVerts = MEM_mallocN(sizeof(*ss->tempVerts)*ss->lenTempArrays, "CCGSubsurf tempVerts");
+       ss->tempEdges = MEM_mallocN(sizeof(*ss->tempEdges)*ss->lenTempArrays, "CCGSubsurf tempEdges");
 
        ss->syncState = eSyncState_Vert;
 
@@ -850,22 +890,23 @@ CCGError ccgSubSurf_syncFaceDel(CCGSubSurf *ss, CCGFaceHDL fHDL) {
        return eCCGError_None;
 }
 
-CCGError ccgSubSurf_syncVert(CCGSubSurf *ss, CCGVertHDL vHDL, void *vertData) {
+CCGError ccgSubSurf_syncVert(CCGSubSurf *ss, CCGVertHDL vHDL, void *vertData, int seam, CCGVert **v_r) {
        void **prevp;
-       CCGVert *v;
+       CCGVert *v = NULL;
+       short seamflag = (seam)? Vert_eSeam: 0;
        
        if (ss->syncState==eSyncState_Partial) {
                v = _ehash_lookupWithPrev(ss->vMap, vHDL, &prevp);
                if (!v) {
-                       v = _vert_new(vHDL, ss->subdivLevels, ss->meshIFC.vertDataSize, ss);
+                       v = _vert_new(vHDL, ss);
                        VertDataCopy(_vert_getCo(v,0,ss->meshIFC.vertDataSize), vertData);
                        _ehash_insert(ss->vMap, (EHEntry*) v);
-                       v->flags = Vert_eEffected;
-               } else if (!VertDataEqual(vertData, _vert_getCo(v, 0, ss->meshIFC.vertDataSize))) {
+                       v->flags = Vert_eEffected|seamflag;
+               } else if (!VertDataEqual(vertData, _vert_getCo(v, 0, ss->meshIFC.vertDataSize)) || ((v->flags & Vert_eSeam) != seamflag)) {
                        int i, j;
 
                        VertDataCopy(_vert_getCo(v,0,ss->meshIFC.vertDataSize), vertData);
-                       v->flags = Vert_eEffected;
+                       v->flags = Vert_eEffected|seamflag;
 
                        for (i=0; i<v->numEdges; i++) {
                                CCGEdge *e = v->edges[i];
@@ -886,15 +927,15 @@ CCGError ccgSubSurf_syncVert(CCGSubSurf *ss, CCGVertHDL vHDL, void *vertData) {
 
                v = _ehash_lookupWithPrev(ss->oldVMap, vHDL, &prevp);
                if (!v) {
-                       v = _vert_new(vHDL, ss->subdivLevels, ss->meshIFC.vertDataSize, ss);
+                       v = _vert_new(vHDL, ss);
                        VertDataCopy(_vert_getCo(v,0,ss->meshIFC.vertDataSize), vertData);
                        _ehash_insert(ss->vMap, (EHEntry*) v);
-                       v->flags = Vert_eEffected;
-               } else if (!VertDataEqual(vertData, _vert_getCo(v, 0, ss->meshIFC.vertDataSize))) {
+                       v->flags = Vert_eEffected|seamflag;
+               } else if (!VertDataEqual(vertData, _vert_getCo(v, 0, ss->meshIFC.vertDataSize)) || ((v->flags & Vert_eSeam) != seamflag)) {
                        *prevp = v->next;
                        _ehash_insert(ss->vMap, (EHEntry*) v);
                        VertDataCopy(_vert_getCo(v,0,ss->meshIFC.vertDataSize), vertData);
-                       v->flags = Vert_eEffected|Vert_eChanged;
+                       v->flags = Vert_eEffected|Vert_eChanged|seamflag;
                } else {
                        *prevp = v->next;
                        _ehash_insert(ss->vMap, (EHEntry*) v);
@@ -902,12 +943,13 @@ CCGError ccgSubSurf_syncVert(CCGSubSurf *ss, CCGVertHDL vHDL, void *vertData) {
                }
        }
 
+       if (v_r) *v_r = v;
        return eCCGError_None;
 }
 
-CCGError ccgSubSurf_syncEdge(CCGSubSurf *ss, CCGEdgeHDL eHDL, CCGVertHDL e_vHDL0, CCGVertHDL e_vHDL1, float crease) {
+CCGError ccgSubSurf_syncEdge(CCGSubSurf *ss, CCGEdgeHDL eHDL, CCGVertHDL e_vHDL0, CCGVertHDL e_vHDL1, float crease, CCGEdge **e_r) {
        void **prevp;
-       CCGEdge *e, *eNew;
+       CCGEdge *e = NULL, *eNew;
 
        if (ss->syncState==eSyncState_Partial) {
                e = _ehash_lookupWithPrev(ss->eMap, eHDL, &prevp);
@@ -915,7 +957,7 @@ CCGError ccgSubSurf_syncEdge(CCGSubSurf *ss, CCGEdgeHDL eHDL, CCGVertHDL e_vHDL0
                        CCGVert *v0 = _ehash_lookup(ss->vMap, e_vHDL0);
                        CCGVert *v1 = _ehash_lookup(ss->vMap, e_vHDL1);
 
-                       eNew = _edge_new(eHDL, v0, v1, crease, ss->subdivLevels, ss->meshIFC.vertDataSize, ss);
+                       eNew = _edge_new(eHDL, v0, v1, crease, ss);
 
                        if (e) {
                                *prevp = eNew;
@@ -940,7 +982,7 @@ CCGError ccgSubSurf_syncEdge(CCGSubSurf *ss, CCGEdgeHDL eHDL, CCGVertHDL e_vHDL0
                if (!e || e->v0->vHDL!=e_vHDL0 || e->v1->vHDL!=e_vHDL1|| e->crease!=crease) {
                        CCGVert *v0 = _ehash_lookup(ss->vMap, e_vHDL0);
                        CCGVert *v1 = _ehash_lookup(ss->vMap, e_vHDL1);
-                       e = _edge_new(eHDL, v0, v1, crease, ss->subdivLevels, ss->meshIFC.vertDataSize, ss);
+                       e = _edge_new(eHDL, v0, v1, crease, ss);
                        _ehash_insert(ss->eMap, (EHEntry*) e);
                        e->v0->flags |= Vert_eEffected;
                        e->v1->flags |= Vert_eEffected;
@@ -955,19 +997,19 @@ CCGError ccgSubSurf_syncEdge(CCGSubSurf *ss, CCGEdgeHDL eHDL, CCGVertHDL e_vHDL0
                }
        }
 
+       if (e_r) *e_r = e;
        return eCCGError_None;
 }
 
-CCGError ccgSubSurf_syncFace(CCGSubSurf *ss, CCGFaceHDL fHDL, int numVerts, CCGVertHDL *vHDLs) {
+CCGError ccgSubSurf_syncFace(CCGSubSurf *ss, CCGFaceHDL fHDL, int numVerts, CCGVertHDL *vHDLs, CCGFace **f_r) {
        void **prevp;
-       CCGFace *f, *fNew;
+       CCGFace *f = NULL, *fNew;
        int j, k, topologyChanged = 0;
 
        if (numVerts>ss->lenTempArrays) {
-               int oldLen = ss->lenTempArrays;
                ss->lenTempArrays = (numVerts<ss->lenTempArrays*2)?ss->lenTempArrays*2:numVerts;
-               ss->tempVerts = CCGSUBSURF_realloc(ss, ss->tempVerts, sizeof(*ss->tempVerts)*ss->lenTempArrays, sizeof(*ss->tempVerts)*oldLen);
-               ss->tempEdges = CCGSUBSURF_realloc(ss, ss->tempEdges, sizeof(*ss->tempEdges)*ss->lenTempArrays, sizeof(*ss->tempEdges)*oldLen);
+               ss->tempVerts = MEM_reallocN(ss->tempVerts, sizeof(*ss->tempVerts)*ss->lenTempArrays);
+               ss->tempEdges = MEM_reallocN(ss->tempEdges, sizeof(*ss->tempEdges)*ss->lenTempArrays);
        }
 
        if (ss->syncState==eSyncState_Partial) {
@@ -988,7 +1030,7 @@ CCGError ccgSubSurf_syncFace(CCGSubSurf *ss, CCGFaceHDL fHDL, int numVerts, CCGV
                }
 
                if (!f || topologyChanged) {
-                       fNew = _face_new(fHDL, ss->tempVerts, ss->tempEdges, numVerts, ss->subdivLevels, ss->meshIFC.vertDataSize, ss);
+                       fNew = _face_new(fHDL, ss->tempVerts, ss->tempEdges, numVerts, ss);
 
                        if (f) {
                                ss->numGrids += numVerts - f->numVerts;
@@ -1025,10 +1067,13 @@ CCGError ccgSubSurf_syncFace(CCGSubSurf *ss, CCGFaceHDL fHDL, int numVerts, CCGV
 
                        if (!ss->tempEdges[k]) {
                                if (ss->allowEdgeCreation) {
-                                       CCGEdge *e = ss->tempEdges[k] = _edge_new((CCGEdgeHDL) -1, ss->tempVerts[k], ss->tempVerts[(k+1)%numVerts], ss->defaultCreaseValue, ss->subdivLevels, ss->meshIFC.vertDataSize, ss);
+                                       CCGEdge *e = ss->tempEdges[k] = _edge_new((CCGEdgeHDL) -1, ss->tempVerts[k], ss->tempVerts[(k+1)%numVerts], ss->defaultCreaseValue, ss);
                                        _ehash_insert(ss->eMap, (EHEntry*) e);
                                        e->v0->flags |= Vert_eEffected;
                                        e->v1->flags |= Vert_eEffected;
+                                       if (ss->meshIFC.edgeUserSize) {
+                                               memcpy(ccgSubSurf_getEdgeUserData(ss, e), ss->defaultEdgeUserData, ss->meshIFC.edgeUserSize);
+                                       }
                                } else {
                                        return eCCGError_InvalidValue;
                                }
@@ -1043,7 +1088,7 @@ CCGError ccgSubSurf_syncFace(CCGSubSurf *ss, CCGFaceHDL fHDL, int numVerts, CCGV
                }
 
                if (!f || topologyChanged) {
-                       f = _face_new(fHDL, ss->tempVerts, ss->tempEdges, numVerts, ss->subdivLevels, ss->meshIFC.vertDataSize, ss);
+                       f = _face_new(fHDL, ss->tempVerts, ss->tempEdges, numVerts, ss);
                        _ehash_insert(ss->fMap, (EHEntry*) f);
                        ss->numGrids += numVerts;
 
@@ -1065,6 +1110,7 @@ CCGError ccgSubSurf_syncFace(CCGSubSurf *ss, CCGFaceHDL fHDL, int numVerts, CCGV
                }
        }
 
+       if (f_r) *f_r = f;
        return eCCGError_None;
 }
 
@@ -1078,8 +1124,8 @@ CCGError ccgSubSurf_processSync(CCGSubSurf *ss) {
                _ehash_free(ss->oldFMap, (EHEntryFreeFP) _face_unlinkMarkAndFree, ss);
                _ehash_free(ss->oldEMap, (EHEntryFreeFP) _edge_unlinkMarkAndFree, ss);
                _ehash_free(ss->oldVMap, (EHEntryFreeFP) _vert_free, ss);
-               CCGSUBSURF_free(ss, ss->tempEdges);
-               CCGSUBSURF_free(ss, ss->tempVerts);
+               MEM_freeN(ss->tempEdges);
+               MEM_freeN(ss->tempVerts);
 
                ss->lenTempArrays = 0;
 
@@ -1097,108 +1143,361 @@ CCGError ccgSubSurf_processSync(CCGSubSurf *ss) {
        return eCCGError_None;
 }
 
-static void ccgSubSurf__sync(CCGSubSurf *ss) {
-       CCGVert **effectedV;
-       CCGEdge **effectedE;
-       CCGFace **effectedF;
-       int numEffectedV, numEffectedE, numEffectedF;
+#define VERT_getNo(e, lvl)                                     _vert_getNo(e, lvl, vertDataSize, normalDataOffset)
+#define EDGE_getNo(e, lvl, x)                          _edge_getNo(e, lvl, x, vertDataSize, normalDataOffset)
+#define FACE_getIFNo(f, lvl, S, x, y)          _face_getIFNo(f, lvl, S, x, y, subdivLevels, vertDataSize, normalDataOffset)
+#define FACE_calcIFNo(f, lvl, S, x, y, no)     _face_calcIFNo(f, lvl, S, x, y, no, subdivLevels, vertDataSize)
+#define FACE_getIENo(f, lvl, S, x)                     _face_getIENo(f, lvl, S, x, subdivLevels, vertDataSize, normalDataOffset)
+static void ccgSubSurf__calcVertNormals(CCGSubSurf *ss,
+       CCGVert **effectedV, CCGEdge **effectedE, CCGFace **effectedF,
+       int numEffectedV, int numEffectedE, int numEffectedF) {
+       int i,ptrIdx;
        int subdivLevels = ss->subdivLevels;
+       int lvl = ss->subdivLevels;
+       int edgeSize = 1 + (1<<lvl);
+       int gridSize = 1 + (1<<(lvl-1));
+       int normalDataOffset = ss->normalDataOffset;
        int vertDataSize = ss->meshIFC.vertDataSize;
-       int i,ptrIdx,cornerIdx;
-       int S,x,y;
-       void *q = ss->q, *r = ss->r;
-       int curLvl, nextLvl;
-       int j;
 
-       effectedV = CCGSUBSURF_alloc(ss, sizeof(*effectedV)*ss->vMap->numEntries);
-       effectedE = CCGSUBSURF_alloc(ss, sizeof(*effectedE)*ss->eMap->numEntries);
-       effectedF = CCGSUBSURF_alloc(ss, sizeof(*effectedF)*ss->fMap->numEntries);
-       numEffectedV = numEffectedE = numEffectedF = 0;
-       for (i=0; i<ss->vMap->curSize; i++) {
-               CCGVert *v = (CCGVert*) ss->vMap->buckets[i];
-               for (; v; v = v->next) {
-                       if (v->flags&Vert_eEffected) {
-                               effectedV[numEffectedV++] = v;
+       #pragma omp parallel for private(ptrIdx) schedule(static)
+       for (ptrIdx=0; ptrIdx<numEffectedF; ptrIdx++) {
+               CCGFace *f = (CCGFace*) effectedF[ptrIdx];
+               int S, x, y;
+               float no[3];
 
-                               for (j=0; j<v->numEdges; j++) {
-                                       CCGEdge *e = v->edges[j];
-                                       if (!(e->flags&Edge_eEffected)) {
-                                               effectedE[numEffectedE++] = e;
-                                               e->flags |= Edge_eEffected;
+               for (S=0; S<f->numVerts; S++) {
+                       for (y=0; y<gridSize-1; y++)
+                               for (x=0; x<gridSize-1; x++)
+                                       NormZero(FACE_getIFNo(f, lvl, S, x, y));
+
+                       if (FACE_getEdges(f)[(S-1+f->numVerts)%f->numVerts]->flags&Edge_eEffected)
+                               for (x=0; x<gridSize-1; x++)
+                                       NormZero(FACE_getIFNo(f, lvl, S, x, gridSize-1));
+                       if (FACE_getEdges(f)[S]->flags&Edge_eEffected)
+                               for (y=0; y<gridSize-1; y++)
+                                       NormZero(FACE_getIFNo(f, lvl, S, gridSize-1, y));
+                       if (FACE_getVerts(f)[S]->flags&Vert_eEffected)
+                               NormZero(FACE_getIFNo(f, lvl, S, gridSize-1, gridSize-1));
+               }
+
+               for (S=0; S<f->numVerts; S++) {
+                       int yLimit = !(FACE_getEdges(f)[(S-1+f->numVerts)%f->numVerts]->flags&Edge_eEffected);
+                       int xLimit = !(FACE_getEdges(f)[S]->flags&Edge_eEffected);
+                       int yLimitNext = xLimit;
+                       int xLimitPrev = yLimit;
+                       
+                       for (y=0; y<gridSize - 1; y++) {
+                               for (x=0; x<gridSize - 1; x++) {
+                                       int xPlusOk = (!xLimit || x<gridSize-2);
+                                       int yPlusOk = (!yLimit || y<gridSize-2);
+
+                                       FACE_calcIFNo(f, lvl, S, x, y, no);
+
+                                       NormAdd(FACE_getIFNo(f, lvl, S, x+0, y+0), no);
+                                       if (xPlusOk)
+                                               NormAdd(FACE_getIFNo(f, lvl, S, x+1, y+0), no);
+                                       if (yPlusOk)
+                                               NormAdd(FACE_getIFNo(f, lvl, S, x+0, y+1), no);
+                                       if (xPlusOk && yPlusOk) {
+                                               if (x<gridSize-2 || y<gridSize-2 || FACE_getVerts(f)[S]->flags&Vert_eEffected) {
+                                                       NormAdd(FACE_getIFNo(f, lvl, S, x+1, y+1), no);
+                                               }
+                                       }
+
+                                       if (x==0 && y==0) {
+                                               int K;
+
+                                               if (!yLimitNext || 1<gridSize-1)
+                                                       NormAdd(FACE_getIFNo(f, lvl, (S+1)%f->numVerts, 0, 1), no);
+                                               if (!xLimitPrev || 1<gridSize-1)
+                                                       NormAdd(FACE_getIFNo(f, lvl, (S-1+f->numVerts)%f->numVerts, 1, 0), no);
+
+                                               for (K=0; K<f->numVerts; K++) {
+                                                       if (K!=S) {
+                                                               NormAdd(FACE_getIFNo(f, lvl, K, 0, 0), no);
+                                                       }
+                                               }
+                                       } else if (y==0) {
+                                               NormAdd(FACE_getIFNo(f, lvl, (S+1)%f->numVerts, 0, x), no);
+                                               if (!yLimitNext || x<gridSize-2)
+                                                       NormAdd(FACE_getIFNo(f, lvl, (S+1)%f->numVerts, 0, x+1), no);
+                                       } else if (x==0) {
+                                               NormAdd(FACE_getIFNo(f, lvl, (S-1+f->numVerts)%f->numVerts, y, 0), no);
+                                               if (!xLimitPrev || y<gridSize-2)
+                                                       NormAdd(FACE_getIFNo(f, lvl, (S-1+f->numVerts)%f->numVerts, y+1, 0), no);
                                        }
                                }
+                       }
+               }
+       }
+               // XXX can I reduce the number of normalisations here?
+       for (ptrIdx=0; ptrIdx<numEffectedV; ptrIdx++) {
+               CCGVert *v = (CCGVert*) effectedV[ptrIdx];
+               float length, *no = _vert_getNo(v, lvl, vertDataSize, normalDataOffset);
 
-                               for (j=0; j<v->numFaces; j++) {
-                                       CCGFace *f = v->faces[j];
-                                       if (!(f->flags&Face_eEffected)) {
-                                               effectedF[numEffectedF++] = f;
-                                               f->flags |= Face_eEffected;
+               NormZero(no);
+
+               for (i=0; i<v->numFaces; i++) {
+                       CCGFace *f = v->faces[i];
+                       NormAdd(no, FACE_getIFNo(f, lvl, _face_getVertIndex(f,v), gridSize-1, gridSize-1));
+               }
+
+               length = sqrt(no[0]*no[0] + no[1]*no[1] + no[2]*no[2]);
+
+               if (length>EPSILON) {
+                       float invLength = 1.0f/length;
+                       no[0] *= invLength;
+                       no[1] *= invLength;
+                       no[2] *= invLength;
+               } else {
+                       NormZero(no);
+               }
+
+               for (i=0; i<v->numFaces; i++) {
+                       CCGFace *f = v->faces[i];
+                       NormCopy(FACE_getIFNo(f, lvl, _face_getVertIndex(f,v), gridSize-1, gridSize-1), no);
+               }
+       }
+       for (ptrIdx=0; ptrIdx<numEffectedE; ptrIdx++) {
+               CCGEdge *e = (CCGEdge*) effectedE[ptrIdx];
+
+               if (e->numFaces) {
+                       CCGFace *fLast = e->faces[e->numFaces-1];
+                       int x;
+
+                       for (i=0; i<e->numFaces-1; i++) {
+                               CCGFace *f = e->faces[i];
+
+                               for (x=1; x<edgeSize-1; x++) {
+                                       NormAdd(_face_getIFNoEdge(fLast, e, lvl, x, 0, subdivLevels, vertDataSize, normalDataOffset),
+                                                       _face_getIFNoEdge(f, e, lvl, x, 0, subdivLevels, vertDataSize, normalDataOffset));
+                               }
+                       }
+
+                       for (i=0; i<e->numFaces-1; i++) {
+                               CCGFace *f = e->faces[i];
+
+                               for (x=1; x<edgeSize-1; x++) {
+                                       NormCopy(_face_getIFNoEdge(f, e, lvl, x, 0, subdivLevels, vertDataSize, normalDataOffset),
+                                                       _face_getIFNoEdge(fLast, e, lvl, x, 0, subdivLevels, vertDataSize, normalDataOffset));
+                               }
+                       }
+               }
+       }
+
+       #pragma omp parallel for private(ptrIdx) schedule(static)
+       for (ptrIdx=0; ptrIdx<numEffectedF; ptrIdx++) {
+               CCGFace *f = (CCGFace*) effectedF[ptrIdx];
+               int S, x, y;
+
+               for (S=0; S<f->numVerts; S++) {
+                       NormCopy(FACE_getIFNo(f, lvl, (S+1)%f->numVerts, 0, gridSize-1),
+                                        FACE_getIFNo(f, lvl, S, gridSize-1, 0));
+               }
+
+               for (S=0; S<f->numVerts; S++) {
+                       for (y=0; y<gridSize; y++) {
+                               for (x=0; x<gridSize; x++) {
+                                       float *no = FACE_getIFNo(f, lvl, S, x, y);
+                                       float length = sqrt(no[0]*no[0] + no[1]*no[1] + no[2]*no[2]);
+
+                                       if (length>EPSILON) {
+                                               float invLength = 1.0f/length;
+                                               no[0] *= invLength;
+                                               no[1] *= invLength;
+                                               no[2] *= invLength;
+                                       } else {
+                                               NormZero(no);
                                        }
                                }
                        }
+
+                       VertDataCopy((float*)((byte*)FACE_getCenterData(f) + normalDataOffset),
+                               FACE_getIFNo(f, lvl, S, 0, 0));
+
+                       for (x=1; x<gridSize-1; x++)
+                               NormCopy(FACE_getIENo(f, lvl, S, x),
+                                       FACE_getIFNo(f, lvl, S, x, 0));
+               }
+       }
+
+       for (ptrIdx=0; ptrIdx<numEffectedE; ptrIdx++) {
+               CCGEdge *e = (CCGEdge*) effectedE[ptrIdx];
+
+               if (e->numFaces) {
+                       CCGFace *f = e->faces[0];
+                       int x;
+
+                       for (x=0; x<edgeSize; x++)
+                               NormCopy(EDGE_getNo(e, lvl, x),
+                                       _face_getIFNoEdge(f, e, lvl, x, 0, subdivLevels, vertDataSize, normalDataOffset));
                }
        }
+}
+#undef FACE_getIFNo
 
 #define VERT_getCo(v, lvl)                             _vert_getCo(v, lvl, vertDataSize)
 #define EDGE_getCo(e, lvl, x)                  _edge_getCo(e, lvl, x, vertDataSize)
 #define FACE_getIECo(f, lvl, S, x)             _face_getIECo(f, lvl, S, x, subdivLevels, vertDataSize)
 #define FACE_getIFCo(f, lvl, S, x, y)  _face_getIFCo(f, lvl, S, x, y, subdivLevels, vertDataSize)
-       curLvl = 0;
-       nextLvl = curLvl+1;
+static void ccgSubSurf__calcSubdivLevel(CCGSubSurf *ss,
+       CCGVert **effectedV, CCGEdge **effectedE, CCGFace **effectedF,
+       int numEffectedV, int numEffectedE, int numEffectedF, int curLvl) {
+       int subdivLevels = ss->subdivLevels;
+       int edgeSize = 1 + (1<<curLvl);
+       int gridSize = 1 + (1<<(curLvl-1));
+       int nextLvl = curLvl+1;
+       int ptrIdx, cornerIdx, i;
+       int vertDataSize = ss->meshIFC.vertDataSize;
+       void *q = ss->q, *r = ss->r;
 
+       #pragma omp parallel for private(ptrIdx) schedule(static)
        for (ptrIdx=0; ptrIdx<numEffectedF; ptrIdx++) {
-               CCGFace *f = effectedF[ptrIdx];
-               void *co = FACE_getCenterData(f);
-               VertDataZero(co);
-               for (i=0; i<f->numVerts; i++) {
-                       VertDataAdd(co, VERT_getCo(FACE_getVerts(f)[i], curLvl));
+               CCGFace *f = (CCGFace*) effectedF[ptrIdx];
+               int S, x, y;
+
+                       /* interior face midpoints
+                        *  o old interior face points
+                        */
+               for (S=0; S<f->numVerts; S++) {
+                       for (y=0; y<gridSize-1; y++) {
+                               for (x=0; x<gridSize-1; x++) {
+                                       int fx = 1 + 2*x;
+                                       int fy = 1 + 2*y;
+                                       void *co0 = FACE_getIFCo(f, curLvl, S, x+0, y+0);
+                                       void *co1 = FACE_getIFCo(f, curLvl, S, x+1, y+0);
+                                       void *co2 = FACE_getIFCo(f, curLvl, S, x+1, y+1);
+                                       void *co3 = FACE_getIFCo(f, curLvl, S, x+0, y+1);
+                                       void *co = FACE_getIFCo(f, nextLvl, S, fx, fy);
+
+                                       VertDataAvg4(co, co0, co1, co2, co3);
+                               }
+                       }
                }
-               VertDataMulN(co, 1.0f/f->numVerts);
 
-               f->flags = 0;
-       }
-       for (ptrIdx=0; ptrIdx<numEffectedE; ptrIdx++) {
-               CCGEdge *e = effectedE[ptrIdx];
-               void *co = EDGE_getCo(e, nextLvl, 1);
-               float sharpness = EDGE_getSharpness(e, curLvl, ss);
+                       /* interior edge midpoints
+                        *  o old interior edge points
+                        *  o new interior face midpoints
+                        */
+               for (S=0; S<f->numVerts; S++) {
+                       for (x=0; x<gridSize-1; x++) {
+                               int fx = x*2 + 1;
+                               void *co0 = FACE_getIECo(f, curLvl, S, x+0);
+                               void *co1 = FACE_getIECo(f, curLvl, S, x+1);
+                               void *co2 = FACE_getIFCo(f, nextLvl, (S+1)%f->numVerts, 1, fx);
+                               void *co3 = FACE_getIFCo(f, nextLvl, S, fx, 1);
+                               void *co = FACE_getIECo(f, nextLvl, S, fx);
+                               
+                               VertDataAvg4(co, co0, co1, co2, co3);
+                       }
 
-               if (_edge_isBoundary(e) || sharpness>=1.0) {
-                       VertDataCopy(co, VERT_getCo(e->v0, curLvl));
-                       VertDataAdd(co, VERT_getCo(e->v1, curLvl));
-                       VertDataMulN(co, 0.5f);
-               } else {
-                       int numFaces = 0;
-                       VertDataCopy(q, VERT_getCo(e->v0, curLvl));
-                       VertDataAdd(q, VERT_getCo(e->v1, curLvl));
-                       for (i=0; i<e->numFaces; i++) {
-                               CCGFace *f = e->faces[i];
-                               VertDataAdd(q, FACE_getCenterData(f));
-                               numFaces++;
+                                       /* interior face interior edge midpoints
+                                        *  o old interior face points
+                                        *  o new interior face midpoints
+                                        */
+
+                               /* vertical */
+                       for (x=1; x<gridSize-1; x++) {
+                               for (y=0; y<gridSize-1; y++) {
+                                       int fx = x*2;
+                                       int fy = y*2+1;
+                                       void *co0 = FACE_getIFCo(f, curLvl, S, x, y+0);
+                                       void *co1 = FACE_getIFCo(f, curLvl, S, x, y+1);
+                                       void *co2 = FACE_getIFCo(f, nextLvl, S, fx-1, fy);
+                                       void *co3 = FACE_getIFCo(f, nextLvl, S, fx+1, fy);
+                                       void *co = FACE_getIFCo(f, nextLvl, S, fx, fy);
+
+                                       VertDataAvg4(co, co0, co1, co2, co3);
+                               }
                        }
-                       VertDataMulN(q, 1.0f/(2.0f+numFaces));
 
-                       VertDataCopy(r, VERT_getCo(e->v0, curLvl));
-                       VertDataAdd(r, VERT_getCo(e->v1, curLvl));
-                       VertDataMulN(r, 0.5f);
+                               /* horizontal */
+                       for (y=1; y<gridSize-1; y++) {
+                               for (x=0; x<gridSize-1; x++) {
+                                       int fx = x*2+1;
+                                       int fy = y*2;
+                                       void *co0 = FACE_getIFCo(f, curLvl, S, x+0, y);
+                                       void *co1 = FACE_getIFCo(f, curLvl, S, x+1, y);
+                                       void *co2 = FACE_getIFCo(f, nextLvl, S, fx, fy-1);
+                                       void *co3 = FACE_getIFCo(f, nextLvl, S, fx, fy+1);
+                                       void *co = FACE_getIFCo(f, nextLvl, S, fx, fy);
 
-                       VertDataCopy(co, q);
-                       VertDataSub(r, q);
-                       VertDataMulN(r, sharpness);
-                       VertDataAdd(co, r);
+                                       VertDataAvg4(co, co0, co1, co2, co3);
+                               }
+                       }
                }
+       }
 
-               // edge flags cleared later
+               /* exterior edge midpoints
+                *  o old exterior edge points
+                *  o new interior face midpoints
+                */
+       for (ptrIdx=0; ptrIdx<numEffectedE; ptrIdx++) {
+               CCGEdge *e = (CCGEdge*) effectedE[ptrIdx];
+               float sharpness = EDGE_getSharpness(e, curLvl);
+               int x, j;
+
+               if (_edge_isBoundary(e) || sharpness>1.0) {
+                       for (x=0; x<edgeSize-1; x++) {
+                               int fx = x*2 + 1;
+                               void *co0 = EDGE_getCo(e, curLvl, x+0);
+                               void *co1 = EDGE_getCo(e, curLvl, x+1);
+                               void *co = EDGE_getCo(e, nextLvl, fx);
+
+                               VertDataCopy(co, co0);
+                               VertDataAdd(co, co1);
+                               VertDataMulN(co, 0.5);
+                       }
+               } else {
+                       for (x=0; x<edgeSize-1; x++) {
+                               int fx = x*2 + 1;
+                               void *co0 = EDGE_getCo(e, curLvl, x+0);
+                               void *co1 = EDGE_getCo(e, curLvl, x+1);
+                               void *co = EDGE_getCo(e, nextLvl, fx);
+                               int numFaces = 0;
+
+                               VertDataCopy(q, co0);
+                               VertDataAdd(q, co1);
+
+                               for (j=0; j<e->numFaces; j++) {
+                                       CCGFace *f = e->faces[j];
+                                       VertDataAdd(q, _face_getIFCoEdge(f, e, nextLvl, fx, 1, subdivLevels, vertDataSize));
+                                       numFaces++;
+                               }
+
+                               VertDataMulN(q, 1.0f/(2.0f+numFaces));
+
+                               VertDataCopy(r, co0);
+                               VertDataAdd(r, co1);
+                               VertDataMulN(r, 0.5);
+
+                               VertDataCopy(co, q);
+                               VertDataSub(r, q);
+                               VertDataMulN(r, sharpness);
+                               VertDataAdd(co, r);
+                       }
+               }
        }
+
+               /* exterior vertex shift
+                *  o old vertex points (shifting)
+                *  o old exterior edge points
+                *  o new interior face midpoints
+                */
        for (ptrIdx=0; ptrIdx<numEffectedV; ptrIdx++) {
-               CCGVert *v = effectedV[ptrIdx];
+               CCGVert *v = (CCGVert*) effectedV[ptrIdx];
                void *co = VERT_getCo(v, curLvl);
                void *nCo = VERT_getCo(v, nextLvl);
                int sharpCount = 0, allSharp = 1;
                float avgSharpness = 0.0;
+               int j, seam = VERT_seam(v), seamEdges = 0;
 
-               for (i=0; i<v->numEdges; i++) {
-                       CCGEdge *e = v->edges[i];
-                       float sharpness = EDGE_getSharpness(e, curLvl, ss);
+               for (j=0; j<v->numEdges; j++) {
+                       CCGEdge *e = v->edges[j];
+                       float sharpness = EDGE_getSharpness(e, curLvl);
+
+                       if (seam && _edge_isBoundary(e))
+                               seamEdges++;
 
                        if (sharpness!=0.0f) {
                                sharpCount++;
@@ -1208,42 +1507,49 @@ static void ccgSubSurf__sync(CCGSubSurf *ss) {
                        }
                }
 
-               avgSharpness /= sharpCount;
-               if (avgSharpness>1.0) {
-                       avgSharpness = 1.0;
+               if(sharpCount) {
+                       avgSharpness /= sharpCount;
+                       if (avgSharpness>1.0) {
+                               avgSharpness = 1.0;
+                       }
                }
 
+               if (seam && seamEdges < 2)
+                       seam = 0;
+
                if (!v->numEdges) {
                        VertDataCopy(nCo, co);
                } else if (_vert_isBoundary(v)) {
                        int numBoundary = 0;
 
                        VertDataZero(r);
-                       for (i=0; i<v->numEdges; i++) {
-                               CCGEdge *e = v->edges[i];
+                       for (j=0; j<v->numEdges; j++) {
+                               CCGEdge *e = v->edges[j];
                                if (_edge_isBoundary(e)) {
-                                       VertDataAdd(r, VERT_getCo(_edge_getOtherVert(e, v), curLvl));
+                                       VertDataAdd(r, _edge_getCoVert(e, v, curLvl, 1, vertDataSize));
                                        numBoundary++;
                                }
                        }
+
                        VertDataCopy(nCo, co);
                        VertDataMulN(nCo, 0.75);
                        VertDataMulN(r, 0.25f/numBoundary);
                        VertDataAdd(nCo, r);
                } else {
+                       int cornerIdx = (1 + (1<<(curLvl))) - 2;
                        int numEdges = 0, numFaces = 0;
 
                        VertDataZero(q);
-                       for (i=0; i<v->numFaces; i++) {
-                               CCGFace *f = v->faces[i];
-                               VertDataAdd(q, FACE_getCenterData(f));
+                       for (j=0; j<v->numFaces; j++) {
+                               CCGFace *f = v->faces[j];
+                               VertDataAdd(q, FACE_getIFCo(f, nextLvl, _face_getVertIndex(f,v), cornerIdx, cornerIdx));
                                numFaces++;
                        }
                        VertDataMulN(q, 1.0f/numFaces);
                        VertDataZero(r);
-                       for (i=0; i<v->numEdges; i++) {
-                               CCGEdge *e = v->edges[i];
-                               VertDataAdd(r, VERT_getCo(_edge_getOtherVert(e, v), curLvl));
+                       for (j=0; j<v->numEdges; j++) {
+                               CCGEdge *e = v->edges[j];
+                               VertDataAdd(r, _edge_getCoVert(e, v, curLvl, 1,vertDataSize));
                                numEdges++;
                        }
                        VertDataMulN(r, 1.0f/numEdges);
@@ -1255,16 +1561,24 @@ static void ccgSubSurf__sync(CCGSubSurf *ss) {
                        VertDataMulN(nCo, 1.0f/numEdges);
                }
 
-               if (sharpCount>1 && v->numFaces) {
+               if ((sharpCount>1 && v->numFaces) || seam) {
                        VertDataZero(q);
 
-                       for (i=0; i<v->numEdges; i++) {
-                               CCGEdge *e = v->edges[i];
-                               float sharpness = EDGE_getSharpness(e, curLvl, ss);
+                       if (seam) {
+                               avgSharpness = 1.0f;
+                               sharpCount = seamEdges;
+                               allSharp = 1;
+                       }
 
-                               if (sharpness != 0.0) {
-                                       CCGVert *oV = _edge_getOtherVert(e, v);
-                                       VertDataAdd(q, VERT_getCo(oV, curLvl));
+                       for (j=0; j<v->numEdges; j++) {
+                               CCGEdge *e = v->edges[j];
+                               float sharpness = EDGE_getSharpness(e, curLvl);
+
+                               if (seam) {
+                                       if (_edge_isBoundary(e))
+                                               VertDataAdd(q, _edge_getCoVert(e, v, curLvl, 1, vertDataSize));
+                               } else if (sharpness != 0.0) {
+                                       VertDataAdd(q, _edge_getCoVert(e, v, curLvl, 1, vertDataSize));
                                }
                        }
 
@@ -1289,673 +1603,860 @@ static void ccgSubSurf__sync(CCGSubSurf *ss) {
                        VertDataMulN(r, avgSharpness);
                        VertDataAdd(nCo, r);
                }
-
-               // vert flags cleared later
        }
 
-       if (ss->useAgeCounts) {
-               for (i=0; i<numEffectedV; i++) {
-                       CCGVert *v = effectedV[i];
-                       byte *userData = ccgSubSurf_getVertUserData(ss, v);
-                       *((int*) &userData[ss->vertUserAgeOffset]) = ss->currentAge;
-               }
+               /* exterior edge interior shift
+                *  o old exterior edge midpoints (shifting)
+                *  o old exterior edge midpoints
+                *  o new interior face midpoints
+                */
+       for (ptrIdx=0; ptrIdx<numEffectedE; ptrIdx++) {
+               CCGEdge *e = (CCGEdge*) effectedE[ptrIdx];
+               float sharpness = EDGE_getSharpness(e, curLvl);
+               int sharpCount = 0;
+               float avgSharpness = 0.0;
+               int x, j;
 
-               for (i=0; i<numEffectedE; i++) {
-                       CCGEdge *e = effectedE[i];
-                       byte *userData = ccgSubSurf_getEdgeUserData(ss, e);
-                       *((int*) &userData[ss->edgeUserAgeOffset]) = ss->currentAge;
+               if (sharpness!=0.0f) {
+                       sharpCount = 2;
+                       avgSharpness += sharpness;
+
+                       if (avgSharpness>1.0) {
+                               avgSharpness = 1.0;
+                       }
+               } else {
+                       sharpCount = 0;
+                       avgSharpness = 0;
                }
 
-               for (i=0; i<numEffectedF; i++) {
-                       CCGFace *f = effectedF[i];
-                       byte *userData = ccgSubSurf_getFaceUserData(ss, f);
-                       *((int*) &userData[ss->faceUserAgeOffset]) = ss->currentAge;
+               if (_edge_isBoundary(e) && (!e->numFaces || sharpCount<2)) {
+                       for (x=1; x<edgeSize-1; x++) {
+                               int fx = x*2;
+                               void *co = EDGE_getCo(e, curLvl, x);
+                               void *nCo = EDGE_getCo(e, nextLvl, fx);
+                               VertDataCopy(r, EDGE_getCo(e, curLvl, x-1));
+                               VertDataAdd(r, EDGE_getCo(e, curLvl, x+1));
+                               VertDataMulN(r, 0.5);
+                               VertDataCopy(nCo, co);
+                               VertDataMulN(nCo, 0.75);
+                               VertDataMulN(r, 0.25);
+                               VertDataAdd(nCo, r);
+                       }
+               } else {
+                       for (x=1; x<edgeSize-1; x++) {
+                               int fx = x*2;
+                               void *co = EDGE_getCo(e, curLvl, x);
+                               void *nCo = EDGE_getCo(e, nextLvl, fx);
+                               int numFaces = 0;
+
+                               VertDataZero(q);
+                               VertDataZero(r);
+                               VertDataAdd(r, EDGE_getCo(e, curLvl, x-1));
+                               VertDataAdd(r, EDGE_getCo(e, curLvl, x+1));
+                               for (j=0; j<e->numFaces; j++) {
+                                       CCGFace *f = e->faces[j];
+                                       VertDataAdd(q, _face_getIFCoEdge(f, e, nextLvl, fx-1, 1, subdivLevels, vertDataSize));
+                                       VertDataAdd(q, _face_getIFCoEdge(f, e, nextLvl, fx+1, 1, subdivLevels, vertDataSize));
+
+                                       VertDataAdd(r, _face_getIFCoEdge(f, e, curLvl, x, 1, subdivLevels, vertDataSize));
+                                       numFaces++;
+                               }
+                               VertDataMulN(q, 1.0/(numFaces*2.0f));
+                               VertDataMulN(r, 1.0/(2.0f + numFaces));
+
+                               VertDataCopy(nCo, co);
+                               VertDataMulN(nCo, (float) numFaces);
+                               VertDataAdd(nCo, q);
+                               VertDataAdd(nCo, r);
+                               VertDataMulN(nCo, 1.0f/(2+numFaces));
+
+                               if (sharpCount==2) {
+                                       VertDataCopy(q, co);
+                                       VertDataMulN(q, 6.0f);
+                                       VertDataAdd(q, EDGE_getCo(e, curLvl, x-1));
+                                       VertDataAdd(q, EDGE_getCo(e, curLvl, x+1));
+                                       VertDataMulN(q, 1/8.0f);
+
+                                       VertDataSub(q, nCo);
+                                       VertDataMulN(q, avgSharpness);
+                                       VertDataAdd(nCo, q);
+                               }
+                       }
                }
        }
 
-       for (i=0; i<numEffectedE; i++) {
-               CCGEdge *e = effectedE[i];
-               VertDataCopy(EDGE_getCo(e, nextLvl, 0), VERT_getCo(e->v0, nextLvl));
-               VertDataCopy(EDGE_getCo(e, nextLvl, 2), VERT_getCo(e->v1, nextLvl));
-       }
-       for (i=0; i<numEffectedF; i++) {
-               CCGFace *f = effectedF[i];
-               for (S=0; S<f->numVerts; S++) {
-                       CCGEdge *e = FACE_getEdges(f)[S];
-                       CCGEdge *prevE = FACE_getEdges(f)[(S+f->numVerts-1)%f->numVerts];
-
-                       VertDataCopy(FACE_getIFCo(f, nextLvl, S, 0, 0), FACE_getCenterData(f));
-                       VertDataCopy(FACE_getIECo(f, nextLvl, S, 0), FACE_getCenterData(f));
-                       VertDataCopy(FACE_getIFCo(f, nextLvl, S, 1, 1), VERT_getCo(FACE_getVerts(f)[S], nextLvl));
-                       VertDataCopy(FACE_getIECo(f, nextLvl, S, 1), EDGE_getCo(FACE_getEdges(f)[S], nextLvl, 1));
+       #pragma omp parallel private(ptrIdx)
+       {
+               void *q, *r;
 
-                       VertDataCopy(FACE_getIFCo(f, nextLvl, S, 1, 0), _edge_getCoVert(e, FACE_getVerts(f)[S], nextLvl, 1, vertDataSize));
-                       VertDataCopy(FACE_getIFCo(f, nextLvl, S, 0, 1), _edge_getCoVert(prevE, FACE_getVerts(f)[S], nextLvl, 1, vertDataSize));
+               #pragma omp critical
+               {
+                       q = MEM_mallocN(ss->meshIFC.vertDataSize, "CCGSubsurf q");
+                       r = MEM_mallocN(ss->meshIFC.vertDataSize, "CCGSubsurf r");
                }
-       }
-
-       for (curLvl=1; curLvl<subdivLevels; curLvl++) {
-               int edgeSize = 1 + (1<<curLvl);
-               int gridSize = 1 + (1<<(curLvl-1));
-               nextLvl = curLvl+1;
 
+               #pragma omp for schedule(static)
                for (ptrIdx=0; ptrIdx<numEffectedF; ptrIdx++) {
                        CCGFace *f = (CCGFace*) effectedF[ptrIdx];
+                       int S, x, y;
 
-                               /* interior face midpoints
-                                *  o old interior face points
-                                */
-                       for (S=0; S<f->numVerts; S++) {
-                               for (y=0; y<gridSize-1; y++) {
-                                       for (x=0; x<gridSize-1; x++) {
-                                               int fx = 1 + 2*x;
-                                               int fy = 1 + 2*y;
-                                               void *co0 = FACE_getIFCo(f, curLvl, S, x+0, y+0);
-                                               void *co1 = FACE_getIFCo(f, curLvl, S, x+1, y+0);
-                                               void *co2 = FACE_getIFCo(f, curLvl, S, x+1, y+1);
-                                               void *co3 = FACE_getIFCo(f, curLvl, S, x+0, y+1);
-                                               void *co = FACE_getIFCo(f, nextLvl, S, fx, fy);
-
-                                               VertDataAvg4(co, co0, co1, co2, co3);
-                                       }
-                               }
-                       }
-
-                               /* interior edge midpoints
+                               /* interior center point shift
+                                *  o old face center point (shifting)
                                 *  o old interior edge points
                                 *  o new interior face midpoints
                                 */
+                       VertDataZero(q);
                        for (S=0; S<f->numVerts; S++) {
-                               for (x=0; x<gridSize-1; x++) {
-                                       int fx = x*2 + 1;
-                                       void *co0 = FACE_getIECo(f, curLvl, S, x+0);
-                                       void *co1 = FACE_getIECo(f, curLvl, S, x+1);
-                                       void *co2 = FACE_getIFCo(f, nextLvl, (S+1)%f->numVerts, 1, fx);
-                                       void *co3 = FACE_getIFCo(f, nextLvl, S, fx, 1);
-                                       void *co = FACE_getIECo(f, nextLvl, S, fx);
-                                       
-                                       VertDataAvg4(co, co0, co1, co2, co3);
-                               }
+                               VertDataAdd(q, FACE_getIFCo(f, nextLvl, S, 1, 1));
+                       }
+                       VertDataMulN(q, 1.0f/f->numVerts);
+                       VertDataZero(r);
+                       for (S=0; S<f->numVerts; S++) {
+                               VertDataAdd(r, FACE_getIECo(f, curLvl, S, 1));
+                       }
+                       VertDataMulN(r, 1.0f/f->numVerts);
 
-                                               /* interior face interior edge midpoints
-                                                *  o old interior face points
-                                                *  o new interior face midpoints
-                                                */
+                       VertDataMulN(FACE_getCenterData(f), f->numVerts-2.0f);
+                       VertDataAdd(FACE_getCenterData(f), q);
+                       VertDataAdd(FACE_getCenterData(f), r);
+                       VertDataMulN(FACE_getCenterData(f), 1.0f/f->numVerts);
 
-                                       /* vertical */
+                       for (S=0; S<f->numVerts; S++) {
+                                       /* interior face shift
+                                        *  o old interior face point (shifting)
+                                        *  o new interior edge midpoints
+                                        *  o new interior face midpoints
+                                        */
                                for (x=1; x<gridSize-1; x++) {
-                                       for (y=0; y<gridSize-1; y++) {
+                                       for (y=1; y<gridSize-1; y++) {
                                                int fx = x*2;
-                                               int fy = y*2+1;
-                                               void *co0 = FACE_getIFCo(f, curLvl, S, x, y+0);
-                                               void *co1 = FACE_getIFCo(f, curLvl, S, x, y+1);
-                                               void *co2 = FACE_getIFCo(f, nextLvl, S, fx-1, fy);
-                                               void *co3 = FACE_getIFCo(f, nextLvl, S, fx+1, fy);
-                                               void *co = FACE_getIFCo(f, nextLvl, S, fx, fy);
-
-                                               VertDataAvg4(co, co0, co1, co2, co3);
+                                               int fy = y*2;
+                                               void *co = FACE_getIFCo(f, curLvl, S, x, y);
+                                               void *nCo = FACE_getIFCo(f, nextLvl, S, fx, fy);
+                                               
+                                               VertDataAvg4(q, FACE_getIFCo(f, nextLvl, S, fx-1, fy-1),
+                                                       FACE_getIFCo(f, nextLvl, S, fx+1, fy-1),
+                                                       FACE_getIFCo(f, nextLvl, S, fx+1, fy+1),
+                                                       FACE_getIFCo(f, nextLvl, S, fx-1, fy+1));
+
+                                               VertDataAvg4(r, FACE_getIFCo(f, nextLvl, S, fx-1, fy+0),
+                                                       FACE_getIFCo(f, nextLvl, S, fx+1, fy+0),
+                                                       FACE_getIFCo(f, nextLvl, S, fx+0, fy-1),
+                                                       FACE_getIFCo(f, nextLvl, S, fx+0, fy+1));
+
+                                               VertDataCopy(nCo, co);
+                                               VertDataSub(nCo, q);
+                                               VertDataMulN(nCo, 0.25f);
+                                               VertDataAdd(nCo, r);
                                        }
                                }
 
-                                       /* horizontal */
-                               for (y=1; y<gridSize-1; y++) {
-                                       for (x=0; x<gridSize-1; x++) {
-                                               int fx = x*2+1;
-                                               int fy = y*2;
-                                               void *co0 = FACE_getIFCo(f, curLvl, S, x+0, y);
-                                               void *co1 = FACE_getIFCo(f, curLvl, S, x+1, y);
-                                               void *co2 = FACE_getIFCo(f, nextLvl, S, fx, fy-1);
-                                               void *co3 = FACE_getIFCo(f, nextLvl, S, fx, fy+1);
-                                               void *co = FACE_getIFCo(f, nextLvl, S, fx, fy);
+                                       /* interior edge interior shift
+                                        *  o old interior edge point (shifting)
+                                        *  o new interior edge midpoints
+                                        *  o new interior face midpoints
+                                        */
+                               for (x=1; x<gridSize-1; x++) {
+                                       int fx = x*2;
+                                       void *co = FACE_getIECo(f, curLvl, S, x);
+                                       void *nCo = FACE_getIECo(f, nextLvl, S, fx);
+                                       
+                                       VertDataAvg4(q, FACE_getIFCo(f, nextLvl, (S+1)%f->numVerts, 1, fx-1),
+                                               FACE_getIFCo(f, nextLvl, (S+1)%f->numVerts, 1, fx+1),
+                                               FACE_getIFCo(f, nextLvl, S, fx+1, +1),
+                                               FACE_getIFCo(f, nextLvl, S, fx-1, +1));
 
-                                               VertDataAvg4(co, co0, co1, co2, co3);
-                                       }
+                                       VertDataAvg4(r, FACE_getIECo(f, nextLvl, S, fx-1),
+                                               FACE_getIECo(f, nextLvl, S, fx+1),
+                                               FACE_getIFCo(f, nextLvl, (S+1)%f->numVerts, 1, fx),
+                                               FACE_getIFCo(f, nextLvl, S, fx, 1));
+
+                                       VertDataCopy(nCo, co);
+                                       VertDataSub(nCo, q);
+                                       VertDataMulN(nCo, 0.25f);
+                                       VertDataAdd(nCo, r);
                                }
                        }
                }
 
-                       /* exterior edge midpoints
-                        *  o old exterior edge points
-                        *  o new interior face midpoints
-                        */
-               for (ptrIdx=0; ptrIdx<numEffectedE; ptrIdx++) {
-                       CCGEdge *e = (CCGEdge*) effectedE[ptrIdx];
-                       float sharpness = EDGE_getSharpness(e, curLvl, ss);
-
-                       if (_edge_isBoundary(e) || sharpness>1.0) {
-                               for (x=0; x<edgeSize-1; x++) {
-                                       int fx = x*2 + 1;
-                                       void *co0 = EDGE_getCo(e, curLvl, x+0);
-                                       void *co1 = EDGE_getCo(e, curLvl, x+1);
-                                       void *co = EDGE_getCo(e, nextLvl, fx);
-
-                                       VertDataCopy(co, co0);
-                                       VertDataAdd(co, co1);
-                                       VertDataMulN(co, 0.5);
+               #pragma omp critical
+               {
+                       MEM_freeN(q);
+                       MEM_freeN(r);
+               }
+       }
+
+               /* copy down */
+       edgeSize = 1 + (1<<(nextLvl));
+       gridSize = 1 + (1<<((nextLvl)-1));
+       cornerIdx = gridSize-1;
+
+       #pragma omp parallel for private(i) schedule(static)
+       for (i=0; i<numEffectedE; i++) {
+               CCGEdge *e = effectedE[i];
+               VertDataCopy(EDGE_getCo(e, nextLvl, 0), VERT_getCo(e->v0, nextLvl));
+               VertDataCopy(EDGE_getCo(e, nextLvl, edgeSize-1), VERT_getCo(e->v1, nextLvl));
+       }
+
+       #pragma omp parallel for private(i) schedule(static)
+       for (i=0; i<numEffectedF; i++) {
+               CCGFace *f = effectedF[i];
+               int S, x;
+
+               for (S=0; S<f->numVerts; S++) {
+                       CCGEdge *e = FACE_getEdges(f)[S];
+                       CCGEdge *prevE = FACE_getEdges(f)[(S+f->numVerts-1)%f->numVerts];
+
+                       VertDataCopy(FACE_getIFCo(f, nextLvl, S, 0, 0), FACE_getCenterData(f));
+                       VertDataCopy(FACE_getIECo(f, nextLvl, S, 0), FACE_getCenterData(f));
+                       VertDataCopy(FACE_getIFCo(f, nextLvl, S, cornerIdx, cornerIdx), VERT_getCo(FACE_getVerts(f)[S], nextLvl));
+                       VertDataCopy(FACE_getIECo(f, nextLvl, S, cornerIdx), EDGE_getCo(FACE_getEdges(f)[S], nextLvl, cornerIdx));
+                       for (x=1; x<gridSize-1; x++) {
+                               void *co = FACE_getIECo(f, nextLvl, S, x);
+                               VertDataCopy(FACE_getIFCo(f, nextLvl, S, x, 0), co);
+                               VertDataCopy(FACE_getIFCo(f, nextLvl, (S+1)%f->numVerts, 0, x), co);
+                       }
+                       for (x=0; x<gridSize-1; x++) {
+                               int eI = gridSize-1-x;
+                               VertDataCopy(FACE_getIFCo(f, nextLvl, S, cornerIdx, x), _edge_getCoVert(e, FACE_getVerts(f)[S], nextLvl, eI,vertDataSize));
+                               VertDataCopy(FACE_getIFCo(f, nextLvl, S, x, cornerIdx), _edge_getCoVert(prevE, FACE_getVerts(f)[S], nextLvl, eI,vertDataSize));
+                       }
+               }
+       }
+}
+
+
+static void ccgSubSurf__sync(CCGSubSurf *ss) {
+       CCGVert **effectedV;
+       CCGEdge **effectedE;
+       CCGFace **effectedF;
+       int numEffectedV, numEffectedE, numEffectedF;
+       int subdivLevels = ss->subdivLevels;
+       int vertDataSize = ss->meshIFC.vertDataSize;
+       int i, j, ptrIdx, S;
+       int curLvl, nextLvl;
+       void *q = ss->q, *r = ss->r;
+
+       effectedV = MEM_mallocN(sizeof(*effectedV)*ss->vMap->numEntries, "CCGSubsurf effectedV");
+       effectedE = MEM_mallocN(sizeof(*effectedE)*ss->eMap->numEntries, "CCGSubsurf effectedE");
+       effectedF = MEM_mallocN(sizeof(*effectedF)*ss->fMap->numEntries, "CCGSubsurf effectedF");
+       numEffectedV = numEffectedE = numEffectedF = 0;
+       for (i=0; i<ss->vMap->curSize; i++) {
+               CCGVert *v = (CCGVert*) ss->vMap->buckets[i];
+               for (; v; v = v->next) {
+                       if (v->flags&Vert_eEffected) {
+                               effectedV[numEffectedV++] = v;
+
+                               for (j=0; j<v->numEdges; j++) {
+                                       CCGEdge *e = v->edges[j];
+                                       if (!(e->flags&Edge_eEffected)) {
+                                               effectedE[numEffectedE++] = e;
+                                               e->flags |= Edge_eEffected;
+                                       }
                                }
-                       } else {
-                               for (x=0; x<edgeSize-1; x++) {
-                                       int fx = x*2 + 1;
-                                       void *co0 = EDGE_getCo(e, curLvl, x+0);
-                                       void *co1 = EDGE_getCo(e, curLvl, x+1);
-                                       void *co = EDGE_getCo(e, nextLvl, fx);
-                                       int numFaces = 0;
-
-                                       VertDataCopy(q, co0);
-                                       VertDataAdd(q, co1);
-
-                                       for (i=0; i<e->numFaces; i++) {
-                                               CCGFace *f = e->faces[i];
-                                               VertDataAdd(q, _face_getIFCoEdge(f, e, nextLvl, fx, 1, subdivLevels, vertDataSize));
-                                               numFaces++;
+
+                               for (j=0; j<v->numFaces; j++) {
+                                       CCGFace *f = v->faces[j];
+                                       if (!(f->flags&Face_eEffected)) {
+                                               effectedF[numEffectedF++] = f;
+                                               f->flags |= Face_eEffected;
                                        }
+                               }
+                       }
+               }
+       }
 
-                                       VertDataMulN(q, 1.0f/(2.0f+numFaces));
+       curLvl = 0;
+       nextLvl = curLvl+1;
 
-                                       VertDataCopy(r, co0);
-                                       VertDataAdd(r, co1);
-                                       VertDataMulN(r, 0.5);
+       for (ptrIdx=0; ptrIdx<numEffectedF; ptrIdx++) {
+               CCGFace *f = effectedF[ptrIdx];
+               void *co = FACE_getCenterData(f);
+               VertDataZero(co);
+               for (i=0; i<f->numVerts; i++) {
+                       VertDataAdd(co, VERT_getCo(FACE_getVerts(f)[i], curLvl));
+               }
+               VertDataMulN(co, 1.0f/f->numVerts);
 
-                                       VertDataCopy(co, q);
-                                       VertDataSub(r, q);
-                                       VertDataMulN(r, sharpness);
-                                       VertDataAdd(co, r);
-                               }
+               f->flags = 0;
+       }
+       for (ptrIdx=0; ptrIdx<numEffectedE; ptrIdx++) {
+               CCGEdge *e = effectedE[ptrIdx];
+               void *co = EDGE_getCo(e, nextLvl, 1);
+               float sharpness = EDGE_getSharpness(e, curLvl);
+
+               if (_edge_isBoundary(e) || sharpness>=1.0) {
+                       VertDataCopy(co, VERT_getCo(e->v0, curLvl));
+                       VertDataAdd(co, VERT_getCo(e->v1, curLvl));
+                       VertDataMulN(co, 0.5f);
+               } else {
+                       int numFaces = 0;
+                       VertDataCopy(q, VERT_getCo(e->v0, curLvl));
+                       VertDataAdd(q, VERT_getCo(e->v1, curLvl));
+                       for (i=0; i<e->numFaces; i++) {
+                               CCGFace *f = e->faces[i];
+                               VertDataAdd(q, FACE_getCenterData(f));
+                               numFaces++;
                        }
+                       VertDataMulN(q, 1.0f/(2.0f+numFaces));
+
+                       VertDataCopy(r, VERT_getCo(e->v0, curLvl));
+                       VertDataAdd(r, VERT_getCo(e->v1, curLvl));
+                       VertDataMulN(r, 0.5f);
+
+                       VertDataCopy(co, q);
+                       VertDataSub(r, q);
+                       VertDataMulN(r, sharpness);
+                       VertDataAdd(co, r);
                }
 
-                       /* exterior vertex shift
-                        *  o old vertex points (shifting)
-                        *  o old exterior edge points
-                        *  o new interior face midpoints
-                        */
-               for (ptrIdx=0; ptrIdx<numEffectedV; ptrIdx++) {
-                       CCGVert *v = (CCGVert*) effectedV[ptrIdx];
-                       void *co = VERT_getCo(v, curLvl);
-                       void *nCo = VERT_getCo(v, nextLvl);
-                       int sharpCount = 0, allSharp = 1;
-                       float avgSharpness = 0.0;
+               // edge flags cleared later
+       }
+       for (ptrIdx=0; ptrIdx<numEffectedV; ptrIdx++) {
+               CCGVert *v = effectedV[ptrIdx];
+               void *co = VERT_getCo(v, curLvl);
+               void *nCo = VERT_getCo(v, nextLvl);
+               int sharpCount = 0, allSharp = 1;
+               float avgSharpness = 0.0;
+               int seam = VERT_seam(v), seamEdges = 0;
 
-                       for (i=0; i<v->numEdges; i++) {
-                               CCGEdge *e = v->edges[i];
-                               float sharpness = EDGE_getSharpness(e, curLvl, ss);
+               for (i=0; i<v->numEdges; i++) {
+                       CCGEdge *e = v->edges[i];
+                       float sharpness = EDGE_getSharpness(e, curLvl);
 
-                               if (sharpness!=0.0f) {
-                                       sharpCount++;
-                                       avgSharpness += sharpness;
-                               } else {
-                                       allSharp = 0;
-                               }
+                       if (seam && _edge_isBoundary(e))
+                               seamEdges++;
+
+                       if (sharpness!=0.0f) {
+                               sharpCount++;
+                               avgSharpness += sharpness;
+                       } else {
+                               allSharp = 0;
                        }
+               }
 
+               if(sharpCount) {
                        avgSharpness /= sharpCount;
                        if (avgSharpness>1.0) {
                                avgSharpness = 1.0;
                        }
+               }
 
-                       if (!v->numEdges) {
-                               VertDataCopy(nCo, co);
-                       } else if (_vert_isBoundary(v)) {
-                               int numBoundary = 0;
+               if (seam && seamEdges < 2)
+                       seam = 0;
 
-                               VertDataZero(r);
-                               for (i=0; i<v->numEdges; i++) {
-                                       CCGEdge *e = v->edges[i];
+               if (!v->numEdges) {
+                       VertDataCopy(nCo, co);
+               } else if (_vert_isBoundary(v)) {
+                       int numBoundary = 0;
+
+                       VertDataZero(r);
+                       for (i=0; i<v->numEdges; i++) {
+                               CCGEdge *e = v->edges[i];
+                               if (_edge_isBoundary(e)) {
+                                       VertDataAdd(r, VERT_getCo(_edge_getOtherVert(e, v), curLvl));
+                                       numBoundary++;
+                               }
+                       }
+                       VertDataCopy(nCo, co);
+                       VertDataMulN(nCo, 0.75);
+                       VertDataMulN(r, 0.25f/numBoundary);
+                       VertDataAdd(nCo, r);
+               } else {
+                       int numEdges = 0, numFaces = 0;
+
+                       VertDataZero(q);
+                       for (i=0; i<v->numFaces; i++) {
+                               CCGFace *f = v->faces[i];
+                               VertDataAdd(q, FACE_getCenterData(f));
+                               numFaces++;
+                       }
+                       VertDataMulN(q, 1.0f/numFaces);
+                       VertDataZero(r);
+                       for (i=0; i<v->numEdges; i++) {
+                               CCGEdge *e = v->edges[i];
+                               VertDataAdd(r, VERT_getCo(_edge_getOtherVert(e, v), curLvl));
+                               numEdges++;
+                       }
+                       VertDataMulN(r, 1.0f/numEdges);
+
+                       VertDataCopy(nCo, co);
+                       VertDataMulN(nCo, numEdges-2.0f);
+                       VertDataAdd(nCo, q);
+                       VertDataAdd(nCo, r);
+                       VertDataMulN(nCo, 1.0f/numEdges);
+               }
+
+               if (sharpCount>1 || seam) {
+                       VertDataZero(q);
+
+                       if (seam) {
+                               avgSharpness = 1.0f;
+                               sharpCount = seamEdges;
+                               allSharp = 1;
+                       }
+
+                       for (i=0; i<v->numEdges; i++) {
+                               CCGEdge *e = v->edges[i];
+                               float sharpness = EDGE_getSharpness(e, curLvl);
+
+                               if (seam) {
                                        if (_edge_isBoundary(e)) {
-                                               VertDataAdd(r, _edge_getCoVert(e, v, curLvl, 1, vertDataSize));
-                                               numBoundary++;
+                                               CCGVert *oV = _edge_getOtherVert(e, v);
+                                               VertDataAdd(q, VERT_getCo(oV, curLvl));
                                        }
+                               } else if (sharpness != 0.0) {
+                                       CCGVert *oV = _edge_getOtherVert(e, v);
+                                       VertDataAdd(q, VERT_getCo(oV, curLvl));
                                }
+                       }
 
-                               VertDataCopy(nCo, co);
-                               VertDataMulN(nCo, 0.75);
-                               VertDataMulN(r, 0.25f/numBoundary);
-                               VertDataAdd(nCo, r);
-                       } else {
-                               int cornerIdx = (1 + (1<<(curLvl))) - 2;
-                               int numEdges = 0, numFaces = 0;
+                       VertDataMulN(q, (float) 1/sharpCount);
 
-                               VertDataZero(q);
-                               for (i=0; i<v->numFaces; i++) {
-                                       CCGFace *f = v->faces[i];
-                                       VertDataAdd(q, FACE_getIFCo(f, nextLvl, _face_getVertIndex(f,v), cornerIdx, cornerIdx));
-                                       numFaces++;
-                               }
-                               VertDataMulN(q, 1.0f/numFaces);
-                               VertDataZero(r);
-                               for (i=0; i<v->numEdges; i++) {
-                                       CCGEdge *e = v->edges[i];
-                                       VertDataAdd(r, _edge_getCoVert(e, v, curLvl, 1,vertDataSize));
-                                       numEdges++;
-                               }
-                               VertDataMulN(r, 1.0f/numEdges);
+                       if (sharpCount!=2 || allSharp) {
+                                       // q = q + (co-q)*avgSharpness
+                               VertDataCopy(r, co);
+                               VertDataSub(r, q);
+                               VertDataMulN(r, avgSharpness);
+                               VertDataAdd(q, r);
+                       }
+
+                               // r = co*.75 + q*.25
+                       VertDataCopy(r, co);
+                       VertDataMulN(r, .75);
+                       VertDataMulN(q, .25);
+                       VertDataAdd(r, q);
+
+                               // nCo = nCo  + (r-nCo)*avgSharpness
+                       VertDataSub(r, nCo);
+                       VertDataMulN(r, avgSharpness);
+                       VertDataAdd(nCo, r);
+               }
+
+               // vert flags cleared later
+       }
+
+       if (ss->useAgeCounts) {
+               for (i=0; i<numEffectedV; i++) {
+                       CCGVert *v = effectedV[i];
+                       byte *userData = ccgSubSurf_getVertUserData(ss, v);
+                       *((int*) &userData[ss->vertUserAgeOffset]) = ss->currentAge;
+               }
+
+               for (i=0; i<numEffectedE; i++) {
+                       CCGEdge *e = effectedE[i];
+                       byte *userData = ccgSubSurf_getEdgeUserData(ss, e);
+                       *((int*) &userData[ss->edgeUserAgeOffset]) = ss->currentAge;
+               }
+
+               for (i=0; i<numEffectedF; i++) {
+                       CCGFace *f = effectedF[i];
+                       byte *userData = ccgSubSurf_getFaceUserData(ss, f);
+                       *((int*) &userData[ss->faceUserAgeOffset]) = ss->currentAge;
+               }
+       }
+
+       for (i=0; i<numEffectedE; i++) {
+               CCGEdge *e = effectedE[i];
+               VertDataCopy(EDGE_getCo(e, nextLvl, 0), VERT_getCo(e->v0, nextLvl));
+               VertDataCopy(EDGE_getCo(e, nextLvl, 2), VERT_getCo(e->v1, nextLvl));
+       }
+       for (i=0; i<numEffectedF; i++) {
+               CCGFace *f = effectedF[i];
+               for (S=0; S<f->numVerts; S++) {
+                       CCGEdge *e = FACE_getEdges(f)[S];
+                       CCGEdge *prevE = FACE_getEdges(f)[(S+f->numVerts-1)%f->numVerts];
+
+                       VertDataCopy(FACE_getIFCo(f, nextLvl, S, 0, 0), FACE_getCenterData(f));
+                       VertDataCopy(FACE_getIECo(f, nextLvl, S, 0), FACE_getCenterData(f));
+                       VertDataCopy(FACE_getIFCo(f, nextLvl, S, 1, 1), VERT_getCo(FACE_getVerts(f)[S], nextLvl));
+                       VertDataCopy(FACE_getIECo(f, nextLvl, S, 1), EDGE_getCo(FACE_getEdges(f)[S], nextLvl, 1));
+
+                       VertDataCopy(FACE_getIFCo(f, nextLvl, S, 1, 0), _edge_getCoVert(e, FACE_getVerts(f)[S], nextLvl, 1, vertDataSize));
+                       VertDataCopy(FACE_getIFCo(f, nextLvl, S, 0, 1), _edge_getCoVert(prevE, FACE_getVerts(f)[S], nextLvl, 1, vertDataSize));
+               }
+       }
+
+       for (curLvl=1; curLvl<subdivLevels; curLvl++) {
+               ccgSubSurf__calcSubdivLevel(ss,
+                       effectedV, effectedE, effectedF,
+                       numEffectedV, numEffectedE, numEffectedF, curLvl);
+       }
+
+       if (ss->calcVertNormals)
+               ccgSubSurf__calcVertNormals(ss,
+                       effectedV, effectedE, effectedF,
+                       numEffectedV, numEffectedE, numEffectedF);
+
+       for (ptrIdx=0; ptrIdx<numEffectedV; ptrIdx++) {
+               CCGVert *v = effectedV[ptrIdx];
+               v->flags = 0;
+       }
+       for (ptrIdx=0; ptrIdx<numEffectedE; ptrIdx++) {
+               CCGEdge *e = effectedE[ptrIdx];
+               e->flags = 0;
+       }
+
+       MEM_freeN(effectedF);
+       MEM_freeN(effectedE);
+       MEM_freeN(effectedV);
+}
+
+static void ccgSubSurf__allFaces(CCGSubSurf *ss, CCGFace ***faces, int *numFaces, int *freeFaces)
+{
+       CCGFace **array;
+       int i, num;
+
+       if(!*faces) {
+               array = MEM_mallocN(sizeof(*array)*ss->fMap->numEntries, "CCGSubsurf allFaces");
+               num = 0;
+               for (i=0; i<ss->fMap->curSize; i++) {
+                       CCGFace *f = (CCGFace*) ss->fMap->buckets[i];
+
+                       for (; f; f = f->next)
+                               array[num++] = f;
+               }
+
+               *faces = array;
+               *numFaces = num;
+               *freeFaces= 1;
+       }
+       else
+               *freeFaces= 0;
+}
+
+static void ccgSubSurf__effectedFaceNeighbours(CCGSubSurf *ss, CCGFace **faces, int numFaces, CCGVert ***verts, int *numVerts, CCGEdge ***edges, int *numEdges)
+{
+       CCGVert **arrayV;
+       CCGEdge **arrayE;
+       int numV, numE, i, j;
+
+       arrayV = MEM_mallocN(sizeof(*arrayV)*ss->vMap->numEntries, "CCGSubsurf arrayV");
+       arrayE = MEM_mallocN(sizeof(*arrayE)*ss->eMap->numEntries, "CCGSubsurf arrayV");
+       numV = numE = 0;
+
+       for (i=0; i<numFaces; i++) {
+               CCGFace *f = faces[i];
+               f->flags |= Face_eEffected;
+       }
+
+       for (i=0; i<ss->vMap->curSize; i++) {
+               CCGVert *v = (CCGVert*) ss->vMap->buckets[i];
+
+               for (; v; v = v->next) {
+                       for(j=0; j<v->numFaces; j++)
+                               if(!(v->faces[j]->flags & Face_eEffected))
+                                       break;
+                       
+                       if(j == v->numFaces) {
+                               arrayV[numV++] = v;
+                               v->flags |= Vert_eEffected;
+                       }
+               }
+       }
 
-                               VertDataCopy(nCo, co);
-                               VertDataMulN(nCo, numEdges-2.0f);
-                               VertDataAdd(nCo, q);
-                               VertDataAdd(nCo, r);
-                               VertDataMulN(nCo, 1.0f/numEdges);
+       for (i=0; i<ss->eMap->curSize; i++) {
+               CCGEdge *e = (CCGEdge*) ss->eMap->buckets[i];
+
+               for (; e; e = e->next) {
+                       for(j=0; j<e->numFaces; j++)
+                               if(!(e->faces[j]->flags & Face_eEffected))
+                                       break;
+                       
+                       if(j == e->numFaces) {
+                               e->flags |= Edge_eEffected;
+                               arrayE[numE++] = e;
                        }
+               }
+       }
 
-                       if (sharpCount>1 && v->numFaces) {
-                               VertDataZero(q);
+       *verts = arrayV;
+       *numVerts = numV;
+       *edges = arrayE;
+       *numEdges = numE;
+}
 
-                               for (i=0; i<v->numEdges; i++) {
-                                       CCGEdge *e = v->edges[i];
-                                       float sharpness = EDGE_getSharpness(e, curLvl, ss);
+/* copy face grid coordinates to other places */
+CCGError ccgSubSurf_updateFromFaces(CCGSubSurf *ss, int lvl, CCGFace **effectedF, int numEffectedF)
+{
+       int i, S, x, gridSize, cornerIdx, subdivLevels;
+       int vertDataSize = ss->meshIFC.vertDataSize, freeF;
 
-                                       if (sharpness != 0.0) {
-                                               VertDataAdd(q, _edge_getCoVert(e, v, curLvl, 1, vertDataSize));
-                                       }
-                               }
+       subdivLevels = ss->subdivLevels;
+       lvl = (lvl)? lvl: subdivLevels;
+       gridSize = 1 + (1<<(lvl-1));
+       cornerIdx = gridSize-1;
 
-                               VertDataMulN(q, (float) 1/sharpCount);
+       ccgSubSurf__allFaces(ss, &effectedF, &numEffectedF, &freeF);
 
-                               if (sharpCount!=2 || allSharp) {
-                                               // q = q + (co-q)*avgSharpness
-                                       VertDataCopy(r, co);
-                                       VertDataSub(r, q);
-                                       VertDataMulN(r, avgSharpness);
-                                       VertDataAdd(q, r);
-                               }
+       for (i=0; i<numEffectedF; i++) {
+               CCGFace *f = effectedF[i];
 
-                                       // r = co*.75 + q*.25
-                               VertDataCopy(r, co);
-                               VertDataMulN(r, .75);
-                               VertDataMulN(q, .25);
-                               VertDataAdd(r, q);
+               for (S=0; S<f->numVerts; S++) {
+                       CCGEdge *e = FACE_getEdges(f)[S];
+                       CCGEdge *prevE = FACE_getEdges(f)[(S+f->numVerts-1)%f->numVerts];
 
-                                       // nCo = nCo  + (r-nCo)*avgSharpness
-                               VertDataSub(r, nCo);
-                               VertDataMulN(r, avgSharpness);
-                               VertDataAdd(nCo, r);
+                       VertDataCopy(FACE_getCenterData(f), FACE_getIFCo(f, lvl, S, 0, 0));
+                       VertDataCopy(VERT_getCo(FACE_getVerts(f)[S], lvl), FACE_getIFCo(f, lvl, S, cornerIdx, cornerIdx));
+
+                       for (x=0; x<gridSize; x++)
+                               VertDataCopy(FACE_getIECo(f, lvl, S, x), FACE_getIFCo(f, lvl, S, x, 0));
+
+                       for (x=0; x<gridSize; x++) {
+                               int eI = gridSize-1-x;
+                               VertDataCopy(_edge_getCoVert(e, FACE_getVerts(f)[S], lvl, eI,vertDataSize), FACE_getIFCo(f, lvl, S, cornerIdx, x));
+                               VertDataCopy(_edge_getCoVert(prevE, FACE_getVerts(f)[S], lvl, eI,vertDataSize), FACE_getIFCo(f, lvl, S, x, cornerIdx));
                        }
                }
+       }
 
-                       /* exterior edge interior shift
-                        *  o old exterior edge midpoints (shifting)
-                        *  o old exterior edge midpoints
-                        *  o new interior face midpoints
-                        */
-               for (ptrIdx=0; ptrIdx<numEffectedE; ptrIdx++) {
-                       CCGEdge *e = (CCGEdge*) effectedE[ptrIdx];
-                       float sharpness = EDGE_getSharpness(e, curLvl, ss);
-                       int sharpCount = 0;
-                       float avgSharpness = 0.0;
+       if(freeF) MEM_freeN(effectedF);
 
-                       if (sharpness!=0.0f) {
-                               sharpCount = 2;
-                               avgSharpness += sharpness;
+       return eCCGError_None;
+}
 
-                               if (avgSharpness>1.0) {
-                                       avgSharpness = 1.0;
-                               }
-                       } else {
-                               sharpCount = 0;
-                               avgSharpness = 0;
-                       }
+/* copy other places to face grid coordinates */
+CCGError ccgSubSurf_updateToFaces(CCGSubSurf *ss, int lvl, CCGFace **effectedF, int numEffectedF)
+{
+       int i, S, x, gridSize, cornerIdx, subdivLevels;
+       int vertDataSize = ss->meshIFC.vertDataSize, freeF;
 
-                       if (_edge_isBoundary(e) && (!e->numFaces || sharpCount<2)) {
-                               for (x=1; x<edgeSize-1; x++) {
-                                       int fx = x*2;
-                                       void *co = EDGE_getCo(e, curLvl, x);
-                                       void *nCo = EDGE_getCo(e, nextLvl, fx);
-                                       VertDataCopy(r, EDGE_getCo(e, curLvl, x-1));
-                                       VertDataAdd(r, EDGE_getCo(e, curLvl, x+1));
-                                       VertDataMulN(r, 0.5);
-                                       VertDataCopy(nCo, co);
-                                       VertDataMulN(nCo, 0.75);
-                                       VertDataMulN(r, 0.25);
-                                       VertDataAdd(nCo, r);
-                               }
-                       } else {
-                               for (x=1; x<edgeSize-1; x++) {
-                                       int fx = x*2;
-                                       void *co = EDGE_getCo(e, curLvl, x);
-                                       void *nCo = EDGE_getCo(e, nextLvl, fx);
-                                       int numFaces = 0;
-
-                                       VertDataZero(q);
-                                       VertDataZero(r);
-                                       VertDataAdd(r, EDGE_getCo(e, curLvl, x-1));
-                                       VertDataAdd(r, EDGE_getCo(e, curLvl, x+1));
-                                       for (i=0; i<e->numFaces; i++) {
-                                               CCGFace *f = e->faces[i];
-                                               VertDataAdd(q, _face_getIFCoEdge(f, e, nextLvl, fx-1, 1, subdivLevels, vertDataSize));
-                                               VertDataAdd(q, _face_getIFCoEdge(f, e, nextLvl, fx+1, 1, subdivLevels, vertDataSize));
-
-                                               VertDataAdd(r, _face_getIFCoEdge(f, e, curLvl, x, 1, subdivLevels, vertDataSize));
-                                               numFaces++;
-                                       }
-                                       VertDataMulN(q, 1.0/(numFaces*2.0f));
-                                       VertDataMulN(r, 1.0/(2.0f + numFaces));
+       subdivLevels = ss->subdivLevels;
+       lvl = (lvl)? lvl: subdivLevels;
+       gridSize = 1 + (1<<(lvl-1));
+       cornerIdx = gridSize-1;
 
-                                       VertDataCopy(nCo, co);
-                                       VertDataMulN(nCo, (float) numFaces);
-                                       VertDataAdd(nCo, q);
-                                       VertDataAdd(nCo, r);
-                                       VertDataMulN(nCo, 1.0f/(2+numFaces));
-
-                                       if (sharpCount==2) {
-                                               VertDataCopy(q, co);
-                                               VertDataMulN(q, 6.0f);
-                                               VertDataAdd(q, EDGE_getCo(e, curLvl, x-1));
-                                               VertDataAdd(q, EDGE_getCo(e, curLvl, x+1));
-                                               VertDataMulN(q, 1/8.0f);
-
-                                               VertDataSub(q, nCo);
-                                               VertDataMulN(q, avgSharpness);
-                                               VertDataAdd(nCo, q);
-                                       }
-                               }
-                       }
-               }
+       ccgSubSurf__allFaces(ss, &effectedF, &numEffectedF, &freeF);
 
-               for (ptrIdx=0; ptrIdx<numEffectedF; ptrIdx++) {
-                       CCGFace *f = (CCGFace*) effectedF[ptrIdx];
+       for (i=0; i<numEffectedF; i++) {
+               CCGFace *f = effectedF[i];
 
-                               /* interior center point shift
-                                *  o old face center point (shifting)
-                                *  o old interior edge points
-                                *  o new interior face midpoints
-                                */
-                       VertDataZero(q);
-                       for (S=0; S<f->numVerts; S++) {
-                               VertDataAdd(q, FACE_getIFCo(f, nextLvl, S, 1, 1));
+               for (S=0; S<f->numVerts; S++) {
+                       int prevS = (S+f->numVerts-1)%f->numVerts;
+                       CCGEdge *e = FACE_getEdges(f)[S];
+                       CCGEdge *prevE = FACE_getEdges(f)[prevS];
+
+                       for (x=0; x<gridSize; x++) {
+                               int eI = gridSize-1-x;
+                               VertDataCopy(FACE_getIFCo(f, lvl, S, cornerIdx, x), _edge_getCoVert(e, FACE_getVerts(f)[S], lvl, eI,vertDataSize));
+                               VertDataCopy(FACE_getIFCo(f, lvl, S, x, cornerIdx), _edge_getCoVert(prevE, FACE_getVerts(f)[S], lvl, eI,vertDataSize));
                        }
-                       VertDataMulN(q, 1.0f/f->numVerts);
-                       VertDataZero(r);
-                       for (S=0; S<f->numVerts; S++) {
-                               VertDataAdd(r, FACE_getIECo(f, curLvl, S, 1));
+
+                       for (x=1; x<gridSize-1; x++) {
+                               VertDataCopy(FACE_getIFCo(f, lvl, S, 0, x), FACE_getIECo(f, lvl, prevS, x));
+                               VertDataCopy(FACE_getIFCo(f, lvl, S, x, 0), FACE_getIECo(f, lvl, S, x));
                        }
-                       VertDataMulN(r, 1.0f/f->numVerts);
 
-                       VertDataMulN(FACE_getCenterData(f), f->numVerts-2.0f);
-                       VertDataAdd(FACE_getCenterData(f), q);
-                       VertDataAdd(FACE_getCenterData(f), r);
-                       VertDataMulN(FACE_getCenterData(f), 1.0f/f->numVerts);
+                       VertDataCopy(FACE_getIFCo(f, lvl, S, 0, 0), FACE_getCenterData(f));
+                       VertDataCopy(FACE_getIFCo(f, lvl, S, cornerIdx, cornerIdx), VERT_getCo(FACE_getVerts(f)[S], lvl));
+               }
+       }
 
-                       for (S=0; S<f->numVerts; S++) {
-                                       /* interior face shift
-                                        *  o old interior face point (shifting)
-                                        *  o new interior edge midpoints
-                                        *  o new interior face midpoints
-                                        */
-                               for (x=1; x<gridSize-1; x++) {
-                                       for (y=1; y<gridSize-1; y++) {
-                                               int fx = x*2;
-                                               int fy = y*2;
-                                               void *co = FACE_getIFCo(f, curLvl, S, x, y);
-                                               void *nCo = FACE_getIFCo(f, nextLvl, S, fx, fy);
-                                               
-                                               VertDataAvg4(q, FACE_getIFCo(f, nextLvl, S, fx-1, fy-1),
-                                                       FACE_getIFCo(f, nextLvl, S, fx+1, fy-1),
-                                                       FACE_getIFCo(f, nextLvl, S, fx+1, fy+1),
-                                                       FACE_getIFCo(f, nextLvl, S, fx-1, fy+1));
+       if(freeF) MEM_freeN(effectedF);
 
-                                               VertDataAvg4(r, FACE_getIFCo(f, nextLvl, S, fx-1, fy+0),
-                                                       FACE_getIFCo(f, nextLvl, S, fx+1, fy+0),
-                                                       FACE_getIFCo(f, nextLvl, S, fx+0, fy-1),
-                                                       FACE_getIFCo(f, nextLvl, S, fx+0, fy+1));
+       return eCCGError_None;
+}
 
-                                               VertDataCopy(nCo, co);
-                                               VertDataSub(nCo, q);
-                                               VertDataMulN(nCo, 0.25f);
-                                               VertDataAdd(nCo, r);
-                                       }
-                               }
+/* stitch together face grids, averaging coordinates at edges
+   and vertices, for multires displacements */
+CCGError ccgSubSurf_stitchFaces(CCGSubSurf *ss, int lvl, CCGFace **effectedF, int numEffectedF)
+{
+       CCGVert **effectedV;
+       CCGEdge **effectedE;
+       int numEffectedV, numEffectedE, freeF;
+       int i, S, x, gridSize, cornerIdx, subdivLevels, edgeSize;
+       int vertDataSize = ss->meshIFC.vertDataSize;
 
-                                       /* interior edge interior shift
-                                        *  o old interior edge point (shifting)
-                                        *  o new interior edge midpoints
-                                        *  o new interior face midpoints
-                                        */
-                               for (x=1; x<gridSize-1; x++) {
-                                       int fx = x*2;
-                                       void *co = FACE_getIECo(f, curLvl, S, x);
-                                       void *nCo = FACE_getIECo(f, nextLvl, S, fx);
-                                       
-                                       VertDataAvg4(q, FACE_getIFCo(f, nextLvl, (S+1)%f->numVerts, 1, fx-1),
-                                               FACE_getIFCo(f, nextLvl, (S+1)%f->numVerts, 1, fx+1),
-                                               FACE_getIFCo(f, nextLvl, S, fx+1, +1),
-                                               FACE_getIFCo(f, nextLvl, S, fx-1, +1));
+       subdivLevels = ss->subdivLevels;
+       lvl = (lvl)? lvl: subdivLevels;
+       gridSize = 1 + (1<<(lvl-1));
+       edgeSize = 1 + (1<<lvl);
+       cornerIdx = gridSize-1;
 
-                                       VertDataAvg4(r, FACE_getIECo(f, nextLvl, S, fx-1),
-                                               FACE_getIECo(f, nextLvl, S, fx+1),
-                                               FACE_getIFCo(f, nextLvl, (S+1)%f->numVerts, 1, fx),
-                                               FACE_getIFCo(f, nextLvl, S, fx, 1));
+       ccgSubSurf__allFaces(ss, &effectedF, &numEffectedF, &freeF);
+       ccgSubSurf__effectedFaceNeighbours(ss, effectedF, numEffectedF,
+               &effectedV, &numEffectedV, &effectedE, &numEffectedE);
 
-                                       VertDataCopy(nCo, co);
-                                       VertDataSub(nCo, q);
-                                       VertDataMulN(nCo, 0.25f);
-                                       VertDataAdd(nCo, r);
-                               }
-                       }
-               }
+       /* zero */
+       for (i=0; i<numEffectedV; i++) {
+               CCGVert *v = effectedV[i];
+               VertDataZero(VERT_getCo(v, lvl));
+       }
 
-                       /* copy down */
-               edgeSize = 1 + (1<<(nextLvl));
-               gridSize = 1 + (1<<((nextLvl)-1));
-               cornerIdx = gridSize-1;
-               for (i=0; i<numEffectedE; i++) {
-                       CCGEdge *e = effectedE[i];
-                       VertDataCopy(EDGE_getCo(e, nextLvl, 0), VERT_getCo(e->v0, nextLvl));
-                       VertDataCopy(EDGE_getCo(e, nextLvl, edgeSize-1), VERT_getCo(e->v1, nextLvl));
-               }
-               for (i=0; i<numEffectedF; i++) {
-                       CCGFace *f = effectedF[i];
-                       for (S=0; S<f->numVerts; S++) {
-                               CCGEdge *e = FACE_getEdges(f)[S];
-                               CCGEdge *prevE = FACE_getEdges(f)[(S+f->numVerts-1)%f->numVerts];
+       for (i=0; i<numEffectedE; i++) {
+               CCGEdge *e = effectedE[i];
 
-                               VertDataCopy(FACE_getIFCo(f, nextLvl, S, 0, 0), FACE_getCenterData(f));
-                               VertDataCopy(FACE_getIECo(f, nextLvl, S, 0), FACE_getCenterData(f));
-                               VertDataCopy(FACE_getIFCo(f, nextLvl, S, cornerIdx, cornerIdx), VERT_getCo(FACE_getVerts(f)[S], nextLvl));
-                               VertDataCopy(FACE_getIECo(f, nextLvl, S, cornerIdx), EDGE_getCo(FACE_getEdges(f)[S], nextLvl, cornerIdx));
-                               for (x=1; x<gridSize-1; x++) {
-                                       void *co = FACE_getIECo(f, nextLvl, S, x);
-                                       VertDataCopy(FACE_getIFCo(f, nextLvl, S, x, 0), co);
-                                       VertDataCopy(FACE_getIFCo(f, nextLvl, (S+1)%f->numVerts, 0, x), co);
-                               }
-                               for (x=0; x<gridSize-1; x++) {
-                                       int eI = gridSize-1-x;
-                                       VertDataCopy(FACE_getIFCo(f, nextLvl, S, cornerIdx, x), _edge_getCoVert(e, FACE_getVerts(f)[S], nextLvl, eI,vertDataSize));
-                                       VertDataCopy(FACE_getIFCo(f, nextLvl, S, x, cornerIdx), _edge_getCoVert(prevE, FACE_getVerts(f)[S], nextLvl, eI,vertDataSize));
-                               }
-                       }
-               }
+               for (x=0; x<edgeSize; x++)
+                       VertDataZero(EDGE_getCo(e, lvl, x));
        }
 
-#define FACE_getIFNo(f, lvl, S, x, y)          _face_getIFNo(f, lvl, S, x, y, subdivLevels, vertDataSize, normalDataOffset)
-#define FACE_calcIFNo(f, lvl, S, x, y, no)     _face_calcIFNo(f, lvl, S, x, y, no, subdivLevels, vertDataSize)
-       if (ss->calcVertNormals) {
-               int lvl = ss->subdivLevels;
-               int edgeSize = 1 + (1<<lvl);
-               int gridSize = 1 + (1<<(lvl-1));
-               int normalDataOffset = ss->normalDataOffset;
+       /* add */
+       for (i=0; i<numEffectedF; i++) {
+               CCGFace *f = effectedF[i];
 
-               for (ptrIdx=0; ptrIdx<numEffectedF; ptrIdx++) {
-                       CCGFace *f = (CCGFace*) effectedF[ptrIdx];
-                       int S, x, y;
+               VertDataZero(FACE_getCenterData(f));
 
-                       for (S=0; S<f->numVerts; S++) {
-                               for (y=0; y<gridSize-1; y++)
-                                       for (x=0; x<gridSize-1; x++)
-                                               NormZero(FACE_getIFNo(f, lvl, S, x, y));
+               for (S=0; S<f->numVerts; S++)
+                       for (x=0; x<gridSize; x++)
+                               VertDataZero(FACE_getIECo(f, lvl, S, x));
+
+               for (S=0; S<f->numVerts; S++) {
+                       int prevS = (S+f->numVerts-1)%f->numVerts;
+                       CCGEdge *e = FACE_getEdges(f)[S];
+                       CCGEdge *prevE = FACE_getEdges(f)[prevS];
 
-                               if (FACE_getEdges(f)[(S-1+f->numVerts)%f->numVerts]->flags&Edge_eEffected)
-                                       for (x=0; x<gridSize-1; x++)
-                                               NormZero(FACE_getIFNo(f, lvl, S, x, gridSize-1));
+                       VertDataAdd(FACE_getCenterData(f), FACE_getIFCo(f, lvl, S, 0, 0));
+                       if (FACE_getVerts(f)[S]->flags&Vert_eEffected)
+                               VertDataAdd(VERT_getCo(FACE_getVerts(f)[S], lvl), FACE_getIFCo(f, lvl, S, cornerIdx, cornerIdx));
+
+                       for (x=1; x<gridSize-1; x++) {
+                               VertDataAdd(FACE_getIECo(f, lvl, S, x), FACE_getIFCo(f, lvl, S, x, 0));
+                               VertDataAdd(FACE_getIECo(f, lvl, prevS, x), FACE_getIFCo(f, lvl, S, 0, x));
+                       }
+
+                       for (x=0; x<gridSize-1; x++) {
+                               int eI = gridSize-1-x;
                                if (FACE_getEdges(f)[S]->flags&Edge_eEffected)
-                                       for (y=0; y<gridSize-1; y++)
-                                               NormZero(FACE_getIFNo(f, lvl, S, gridSize-1, y));
-                               if (FACE_getVerts(f)[S]->flags&Vert_eEffected)
-                                       NormZero(FACE_getIFNo(f, lvl, S, gridSize-1, gridSize-1));
+                                       VertDataAdd(_edge_getCoVert(e, FACE_getVerts(f)[S], lvl, eI,vertDataSize), FACE_getIFCo(f, lvl, S, cornerIdx, x));
+                               if (FACE_getEdges(f)[prevS]->flags&Edge_eEffected)
+                                       if(x != 0)
+                                               VertDataAdd(_edge_getCoVert(prevE, FACE_getVerts(f)[S], lvl, eI,vertDataSize), FACE_getIFCo(f, lvl, S, x, cornerIdx));
                        }
                }
+       }
 
-               for (ptrIdx=0; ptrIdx<numEffectedF; ptrIdx++) {
-                       CCGFace *f = (CCGFace*) effectedF[ptrIdx];
-                       int S, x, y;
-                       float no[3];
+       /* average */
+       for (i=0; i<numEffectedV; i++) {
+               CCGVert *v = effectedV[i];
+               VertDataMulN(VERT_getCo(v, lvl), 1.0f/v->numFaces);
+       }
 
-                       for (S=0; S<f->numVerts; S++) {
-                               int yLimit = !(FACE_getEdges(f)[(S-1+f->numVerts)%f->numVerts]->flags&Edge_eEffected);
-                               int xLimit = !(FACE_getEdges(f)[S]->flags&Edge_eEffected);
-                               int yLimitNext = xLimit;
-                               int xLimitPrev = yLimit;
-                               
-                               for (y=0; y<gridSize - 1; y++) {
-                                       for (x=0; x<gridSize - 1; x++) {
-                                               int xPlusOk = (!xLimit || x<gridSize-2);
-                                               int yPlusOk = (!yLimit || y<gridSize-2);
-
-                                               FACE_calcIFNo(f, lvl, S, x, y, no);
-
-                                               NormAdd(FACE_getIFNo(f, lvl, S, x+0, y+0), no);
-                                               if (xPlusOk)
-                                                       NormAdd(FACE_getIFNo(f, lvl, S, x+1, y+0), no);
-                                               if (yPlusOk)
-                                                       NormAdd(FACE_getIFNo(f, lvl, S, x+0, y+1), no);
-                                               if (xPlusOk && yPlusOk) {
-                                                       if (x<gridSize-2 || y<gridSize-2 || FACE_getVerts(f)[S]->flags&Vert_eEffected) {
-                                                               NormAdd(FACE_getIFNo(f, lvl, S, x+1, y+1), no);
-                                                       }
-                                               }
+       for (i=0; i<numEffectedE; i++) {
+               CCGEdge *e = effectedE[i];
 
-                                               if (x==0 && y==0) {
-                                                       int K;
+               VertDataCopy(EDGE_getCo(e, lvl, 0), VERT_getCo(e->v0, lvl));
+               VertDataCopy(EDGE_getCo(e, lvl, edgeSize-1), VERT_getCo(e->v1, lvl));
 
-                                                       if (!yLimitNext || 1<gridSize-1)
-                                                               NormAdd(FACE_getIFNo(f, lvl, (S+1)%f->numVerts, 0, 1), no);
-                                                       if (!xLimitPrev || 1<gridSize-1)
-                                                               NormAdd(FACE_getIFNo(f, lvl, (S-1+f->numVerts)%f->numVerts, 1, 0), no);
+               for (x=1; x<edgeSize-1; x++)
+                       VertDataMulN(EDGE_getCo(e, lvl, x), 1.0f/e->numFaces);
+       }
 
-                                                       for (K=0; K<f->numVerts; K++) {
-                                                               if (K!=S) {
-                                                                       NormAdd(FACE_getIFNo(f, lvl, K, 0, 0), no);
-                                                               }
-                                                       }
-                                               } else if (y==0) {
-                                                       NormAdd(FACE_getIFNo(f, lvl, (S+1)%f->numVerts, 0, x), no);
-                                                       if (!yLimitNext || x<gridSize-2)
-                                                               NormAdd(FACE_getIFNo(f, lvl, (S+1)%f->numVerts, 0, x+1), no);
-                                               } else if (x==0) {
-                                                       NormAdd(FACE_getIFNo(f, lvl, (S-1+f->numVerts)%f->numVerts, y, 0), no);
-                                                       if (!xLimitPrev || y<gridSize-2)
-                                                               NormAdd(FACE_getIFNo(f, lvl, (S-1+f->numVerts)%f->numVerts, y+1, 0), no);
-                                               }
-                                       }
-                               }
-                       }
-               }
-                       // XXX can I reduce the number of normalisations here?
-               for (ptrIdx=0; ptrIdx<numEffectedV; ptrIdx++) {
-                       CCGVert *v = (CCGVert*) effectedV[ptrIdx];
-                       float length, *no = _vert_getNo(v, lvl, vertDataSize, normalDataOffset);
+       /* copy */
+       for (i=0; i<numEffectedF; i++) {
+               CCGFace *f = effectedF[i];
 
-                       NormZero(no);
+               VertDataMulN(FACE_getCenterData(f), 1.0f/f->numVerts);
 
-                       for (i=0; i<v->numFaces; i++) {
-                               CCGFace *f = v->faces[i];
-                               NormAdd(no, FACE_getIFNo(f, lvl, _face_getVertIndex(f,v), gridSize-1, gridSize-1));
-                       }
+               for (S=0; S<f->numVerts; S++)
+                       for (x=1; x<gridSize-1; x++)
+                               VertDataMulN(FACE_getIECo(f, lvl, S, x), 0.5f);
+
+               for (S=0; S<f->numVerts; S++) {
+                       int prevS = (S+f->numVerts-1)%f->numVerts;
+                       CCGEdge *e = FACE_getEdges(f)[S];
+                       CCGEdge *prevE = FACE_getEdges(f)[prevS];
 
-                       length = sqrt(no[0]*no[0] + no[1]*no[1] + no[2]*no[2]);
+                       VertDataCopy(FACE_getIFCo(f, lvl, S, 0, 0), FACE_getCenterData(f));
+                       VertDataCopy(FACE_getIFCo(f, lvl, S, cornerIdx, cornerIdx), VERT_getCo(FACE_getVerts(f)[S], lvl));
 
-                       if (length>FLT_EPSILON) {
-                               float invLength = 1.0f/length;
-                               no[0] *= invLength;
-                               no[1] *= invLength;
-                               no[2] *= invLength;
-                       } else {
-                               NormZero(no);
+                       for (x=1; x<gridSize-1; x++) {
+                               VertDataCopy(FACE_getIFCo(f, lvl, S, x, 0), FACE_getIECo(f, lvl, S, x));
+                               VertDataCopy(FACE_getIFCo(f, lvl, S, 0, x), FACE_getIECo(f, lvl, prevS, x));
                        }
 
-                       for (i=0; i<v->numFaces; i++) {
-                               CCGFace *f = v->faces[i];
-                               NormCopy(FACE_getIFNo(f, lvl, _face_getVertIndex(f,v), gridSize-1, gridSize-1), no);
+                       for (x=0; x<gridSize-1; x++) {
+                               int eI = gridSize-1-x;
+                               VertDataCopy(FACE_getIFCo(f, lvl, S, cornerIdx, x), _edge_getCoVert(e, FACE_getVerts(f)[S], lvl, eI,vertDataSize));
+                               VertDataCopy(FACE_getIFCo(f, lvl, S, x, cornerIdx), _edge_getCoVert(prevE, FACE_getVerts(f)[S], lvl, eI,vertDataSize));
                        }
+
+                       VertDataCopy(FACE_getIECo(f, lvl, S, 0), FACE_getCenterData(f));
+                       VertDataCopy(FACE_getIECo(f, lvl, S, gridSize-1), FACE_getIFCo(f, lvl, S, gridSize-1, 0));
                }
-               for (ptrIdx=0; ptrIdx<numEffectedE; ptrIdx++) {
-                       CCGEdge *e = (CCGEdge*) effectedE[ptrIdx];
+       }
 
-                       if (e->numFaces) {
-                               CCGFace *fLast = e->faces[e->numFaces-1];
-                               int x;
+       for (i=0; i<numEffectedV; i++)
+               effectedV[i]->flags = 0;
+       for (i=0; i<numEffectedE; i++)
+               effectedE[i]->flags = 0;
+       for (i=0; i<numEffectedF; i++)
+               effectedF[i]->flags = 0;
 
-                               for (i=0; i<e->numFaces-1; i++) {
-                                       CCGFace *f = e->faces[i];
+       MEM_freeN(effectedE);
+       MEM_freeN(effectedV);
+       if(freeF) MEM_freeN(effectedF);
 
-                                       for (x=1; x<edgeSize-1; x++) {
-                                               NormAdd(_face_getIFNoEdge(fLast, e, lvl, x, 0, subdivLevels, vertDataSize, normalDataOffset),
-                                                               _face_getIFNoEdge(f, e, lvl, x, 0, subdivLevels, vertDataSize, normalDataOffset));
-                                       }
-                               }
+       return eCCGError_None;
+}
 
-                               for (i=0; i<e->numFaces-1; i++) {
-                                       CCGFace *f = e->faces[i];
+/* update normals for specified faces */
+CCGError ccgSubSurf_updateNormals(CCGSubSurf *ss, CCGFace **effectedF, int numEffectedF) {
+       CCGVert **effectedV;
+       CCGEdge **effectedE;
+       int i, numEffectedV, numEffectedE, freeF;
 
-                                       for (x=1; x<edgeSize-1; x++) {
-                                               NormCopy(_face_getIFNoEdge(f, e, lvl, x, 0, subdivLevels, vertDataSize, normalDataOffset),
-                                                               _face_getIFNoEdge(fLast, e, lvl, x, 0, subdivLevels, vertDataSize, normalDataOffset));
-                                       }
-                               }
-                       }
-               }
-               for (ptrIdx=0; ptrIdx<numEffectedF; ptrIdx++) {
-                       CCGFace *f = (CCGFace*) effectedF[ptrIdx];
-                       int S;
+       ccgSubSurf__allFaces(ss, &effectedF, &numEffectedF, &freeF);
+       ccgSubSurf__effectedFaceNeighbours(ss, effectedF, numEffectedF,
+               &effectedV, &numEffectedV, &effectedE, &numEffectedE);
 
-                       for (S=0; S<f->numVerts; S++) {
-                               NormCopy(FACE_getIFNo(f, lvl, (S+1)%f->numVerts, 0, gridSize-1),
-                                                FACE_getIFNo(f, lvl, S, gridSize-1, 0));
-                       }
-               }
-               for (ptrIdx=0; ptrIdx<numEffectedF; ptrIdx++) {
-                       CCGFace *f = (CCGFace*) effectedF[ptrIdx];
-                       int S, x, y;
+       if (ss->calcVertNormals)
+               ccgSubSurf__calcVertNormals(ss,
+                       effectedV, effectedE, effectedF,
+                       numEffectedV, numEffectedE, numEffectedF);
 
-                       for (S=0; S<f->numVerts; S++) {
-                               for (y=0; y<gridSize; y++) {
-                                       for (x=0; x<gridSize; x++) {
-                                               float *no = FACE_getIFNo(f, lvl, S, x, y);
-                                               float length = sqrt(no[0]*no[0] + no[1]*no[1] + no[2]*no[2]);
-
-                                               if (length>FLT_EPSILON) {
-                                                       float invLength = 1.0f/length;
-                                                       no[0] *= invLength;
-                                                       no[1] *= invLength;
-                                                       no[2] *= invLength;
-                                               } else {
-                                                       NormZero(no);
-                                               }
-                                       }
-                               }
-                       }
-               }
-       }
-#undef FACE_getIFNo
+       for (i=0; i<numEffectedV; i++)
+               effectedV[i]->flags = 0;
+       for (i=0; i<numEffectedE; i++)
+               effectedE[i]->flags = 0;
+       for (i=0; i<numEffectedF; i++)
+               effectedF[i]->flags = 0;
 
-       for (ptrIdx=0; ptrIdx<numEffectedV; ptrIdx++) {
-               CCGVert *v = effectedV[ptrIdx];
-               v->flags = 0;
-       }
-       for (ptrIdx=0; ptrIdx<numEffectedE; ptrIdx++) {
-               CCGEdge *e = effectedE[ptrIdx];
-               e->flags = 0;
+       MEM_freeN(effectedE);
+       MEM_freeN(effectedV);
+       if(freeF) MEM_freeN(effectedF);
+
+       return eCCGError_None;
+}
+
+/* compute subdivision levels from a given starting point, used by
+   multires subdivide/propagate, by filling in coordinates at a
+   certain level, and then subdividing that up to the highest level */
+CCGError ccgSubSurf_updateLevels(CCGSubSurf *ss, int lvl, CCGFace **effectedF, int numEffectedF)
+{
+       CCGVert **effectedV;
+       CCGEdge **effectedE;
+       int numEffectedV, numEffectedE, freeF, i;
+       int curLvl, subdivLevels = ss->subdivLevels;
+
+       ccgSubSurf__allFaces(ss, &effectedF, &numEffectedF, &freeF);
+       ccgSubSurf__effectedFaceNeighbours(ss, effectedF, numEffectedF,
+               &effectedV, &numEffectedV, &effectedE, &numEffectedE);
+
+       for (curLvl=lvl; curLvl<subdivLevels; curLvl++) {
+               ccgSubSurf__calcSubdivLevel(ss,
+                       effectedV, effectedE, effectedF,
+                       numEffectedV, numEffectedE, numEffectedF, curLvl);
        }
 
+       for (i=0; i<numEffectedV; i++)
+               effectedV[i]->flags = 0;
+       for (i=0; i<numEffectedE; i++)
+               effectedE[i]->flags = 0;
+       for (i=0; i<numEffectedF; i++)
+               effectedF[i]->flags = 0;
+
+       MEM_freeN(effectedE);
+       MEM_freeN(effectedV);
+       if(freeF) MEM_freeN(effectedF);
+
+       return eCCGError_None;
+}
+
 #undef VERT_getCo
 #undef EDGE_getCo
 #undef FACE_getIECo
 #undef FACE_getIFCo
 
-       CCGSUBSURF_free(ss, effectedF);
-       CCGSUBSURF_free(ss, effectedE);
-       CCGSUBSURF_free(ss, effectedV);
-}
-
 /*** External API accessor functions ***/
 
 int ccgSubSurf_getNumVerts(CCGSubSurf *ss) {
@@ -2004,7 +2505,7 @@ int ccgSubSurf_getGridLevelSize(CCGSubSurf *ss, int level) {
 
 /* Vert accessors */
 
-CCGVertHDL ccgSubSurf_getVertVertHandle(CCGSubSurf *ss, CCGVert *v) {
+CCGVertHDL ccgSubSurf_getVertVertHandle(CCGVert *v) {
        return v->vHDL;
 }
 int ccgSubSurf_getVertAge(CCGSubSurf *ss, CCGVert *v) {
@@ -2018,20 +2519,20 @@ int ccgSubSurf_getVertAge(CCGSubSurf *ss, CCGVert *v) {
 void *ccgSubSurf_getVertUserData(CCGSubSurf *ss, CCGVert *v) {
        return VERT_getLevelData(v) + ss->meshIFC.vertDataSize*(ss->subdivLevels+1);
 }
-int ccgSubSurf_getVertNumFaces(CCGSubSurf *ss, CCGVert *v) {
+int ccgSubSurf_getVertNumFaces(CCGVert *v) {
        return v->numFaces;
 }
-CCGFace *ccgSubSurf_getVertFace(CCGSubSurf *ss, CCGVert *v, int index) {
+CCGFace *ccgSubSurf_getVertFace(CCGVert *v, int index) {
        if (index<0 || index>=v->numFaces) {
                return NULL;
        } else {
                return v->faces[index];
        }
 }
-int ccgSubSurf_getVertNumEdges(CCGSubSurf *ss, CCGVert *v) {
+int ccgSubSurf_getVertNumEdges(CCGVert *v) {
        return v->numEdges;
 }
-CCGEdge *ccgSubSurf_getVertEdge(CCGSubSurf *ss, CCGVert *v, int index) {
+CCGEdge *ccgSubSurf_getVertEdge(CCGVert *v, int index) {
        if (index<0 || index>=v->numEdges) {
                return NULL;
        } else {
@@ -2051,7 +2552,7 @@ void *ccgSubSurf_getVertLevelData(CCGSubSurf *ss, CCGVert *v, int level) {
 
 /* Edge accessors */
 
-CCGEdgeHDL ccgSubSurf_getEdgeEdgeHandle(CCGSubSurf *ss, CCGEdge *e) {
+CCGEdgeHDL ccgSubSurf_getEdgeEdgeHandle(CCGEdge *e) {
        return e->eHDL;
 }
 int ccgSubSurf_getEdgeAge(CCGSubSurf *ss, CCGEdge *e) {
@@ -2065,20 +2566,20 @@ int ccgSubSurf_getEdgeAge(CCGSubSurf *ss, CCGEdge *e) {
 void *ccgSubSurf_getEdgeUserData(CCGSubSurf *ss, CCGEdge *e) {
        return EDGE_getLevelData(e) + ss->meshIFC.vertDataSize *((ss->subdivLevels+1) + (1<<(ss->subdivLevels+1))-1);
 }
-int ccgSubSurf_getEdgeNumFaces(CCGSubSurf *ss, CCGEdge *e) {
+int ccgSubSurf_getEdgeNumFaces(CCGEdge *e) {
        return e->numFaces;
 }
-CCGFace *ccgSubSurf_getEdgeFace(CCGSubSurf *ss, CCGEdge *e, int index) {
+CCGFace *ccgSubSurf_getEdgeFace(CCGEdge *e, int index) {
        if (index<0 || index>=e->numFaces) {
                return NULL;
        } else {
                return e->faces[index];
        }
 }
-CCGVert *ccgSubSurf_getEdgeVert0(CCGSubSurf *ss, CCGEdge *e) {
+CCGVert *ccgSubSurf_getEdgeVert0(CCGEdge *e) {
        return e->v0;
 }
-CCGVert *ccgSubSurf_getEdgeVert1(CCGSubSurf *ss, CCGEdge *e) {
+CCGVert *ccgSubSurf_getEdgeVert1(CCGEdge *e) {
        return e->v1;
 }
 void *ccgSubSurf_getEdgeDataArray(CCGSubSurf *ss, CCGEdge *e) {
@@ -2094,6 +2595,9 @@ void *ccgSubSurf_getEdgeLevelData(CCGSubSurf *ss, CCGEdge *e, int x, int level)
                return _edge_getCo(e, level, x, ss->meshIFC.vertDataSize);
        }
 }
+float ccgSubSurf_getEdgeCrease(CCGEdge *e) {
+       return e->crease;
+}
 
 /* Face accessors */
 
@@ -2112,7 +2616,7 @@ void *ccgSubSurf_getFaceUserData(CCGSubSurf *ss, CCGFace *f) {
        int maxGridSize = 1 + (1<<(ss->subdivLevels-1));
        return FACE_getCenterData(f) + ss->meshIFC.vertDataSize *(1 + f->numVerts*maxGridSize + f->numVerts*maxGridSize*maxGridSize);
 }
-int ccgSubSurf_getFaceNumVerts(CCGSubSurf *ss, CCGFace *f) {
+int ccgSubSurf_getFaceNumVerts(CCGFace *f) {
        return f->numVerts;
 }
 CCGVert *ccgSubSurf_getFaceVert(CCGSubSurf *ss, CCGFace *f, int index) {
@@ -2129,7 +2633,16 @@ CCGEdge *ccgSubSurf_getFaceEdge(CCGSubSurf *ss, CCGFace *f, int index) {
                return FACE_getEdges(f)[index];
        }
 }
-void *ccgSubSurf_getFaceCenterData(CCGSubSurf *ss, CCGFace *f) {
+int ccgSubSurf_getFaceEdgeIndex(CCGFace *f, CCGEdge *e) {
+       int i;
+
+       for (i=0; i<f->numVerts; i++)
+               if (FACE_getEdges(f)[i]==e)
+                       return i;
+
+       return -1;
+}
+void *ccgSubSurf_getFaceCenterData(CCGFace *f) {
        return FACE_getCenterData(f);
 }
 void *ccgSubSurf_getFaceGridEdgeDataArray(CCGSubSurf *ss, CCGFace *f, int gridIndex) {