svn merge -r 13452:14721 https://svn.blender.org/svnroot/bf-blender/trunk/blender
[blender.git] / source / blender / blenkernel / intern / CCGSubSurf.c
index 00bee59732cb0daa3023974caea029ebd770fbfa..9dcb6b6e7fab1392dbaaebbf07259c59b9be5623 100644 (file)
@@ -211,6 +211,10 @@ static int VertDataEqual(float *a, float *b) {
                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]; }
+
 
 static int _edge_isBoundary(CCGEdge *e);
 
@@ -219,6 +223,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),
@@ -285,8 +290,7 @@ struct _CCGSubSurf {
        EHash *fMap;    /* map of CCGFaceHDL -> Face */
 
        CCGMeshIFC meshIFC;
-       void *meshData;
-
+       
        CCGAllocatorIFC allocatorIFC;
        CCGAllocatorHDL allocator;
 
@@ -294,6 +298,7 @@ struct _CCGSubSurf {
        int numGrids;
        int allowEdgeCreation;
        float defaultCreaseValue;
+       void *defaultEdgeUserData;
 
        void *q, *r;
                
@@ -386,6 +391,9 @@ static int _vert_isBoundary(CCGVert *v) {
 static void *_vert_getCo(CCGVert *v, int lvl, int dataSize) {
        return &VERT_getLevelData(v)[lvl*dataSize];
 }
+static float *_vert_getNo(CCGVert *v, int lvl, int dataSize, int normalDataOffset) {
+       return (float*) &VERT_getLevelData(v)[lvl*dataSize + normalDataOffset];
+}
 
 static void _vert_free(CCGVert *v, CCGSubSurf *ss) {
        CCGSUBSURF_free(ss, v->edges);
@@ -393,6 +401,10 @@ static void _vert_free(CCGVert *v, CCGSubSurf *ss) {
        CCGSUBSURF_free(ss, v);
 }
 
+static int VERT_seam(CCGVert *v, CCGSubSurf *ss) {
+       return ((v->flags & Vert_eSeam) != 0);
+}
+
 /***/
 
 static CCGEdge *_edge_new(CCGEdgeHDL eHDL, CCGVert *v0, CCGVert *v1, float crease, int levels, int dataSize, CCGSubSurf *ss) {
@@ -444,11 +456,12 @@ static void *_edge_getCo(CCGEdge *e, int lvl, int x, int dataSize) {
        int levelBase = lvl + (1<<lvl) - 1;
        return &EDGE_getLevelData(e)[dataSize*(levelBase + x)];
 }
+#if 0
 static float *_edge_getNo(CCGEdge *e, int lvl, int x, int dataSize, int normalDataOffset) {
        int levelBase = lvl + (1<<lvl) - 1;
        return (float*) &EDGE_getLevelData(e)[dataSize*(levelBase + x) + normalDataOffset];
 }
-
+#endif
 static void *_edge_getCoVert(CCGEdge *e, CCGVert *v, int lvl, int x, int dataSize) {
        int levelBase = lvl + (1<<lvl) - 1;
        if (v==e->v0) {
@@ -470,20 +483,17 @@ static void _edge_unlinkMarkAndFree(CCGEdge *e, CCGSubSurf *ss) {
        _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) {
        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);
@@ -566,6 +576,31 @@ static void *_face_getIFCoEdge(CCGFace *f, CCGEdge *e, int lvl, int eX, int eY,
 static float *_face_getIFNoEdge(CCGFace *f, CCGEdge *e, int lvl, int eX, int eY, int levels, int dataSize, int normalDataOffset) {
        return (float*) ((byte*) _face_getIFCoEdge(f, e, lvl, eX, eY, levels, dataSize) + normalDataOffset);
 }
+void _face_calcIFNo(CCGFace *f, int lvl, int S, int x, int y, float *no, int levels, int dataSize) {
+       float *a = _face_getIFCo(f, lvl, S, x+0, y+0, levels, dataSize);
+       float *b = _face_getIFCo(f, lvl, S, x+1, y+0, levels, dataSize);
+       float *c = _face_getIFCo(f, lvl, S, x+1, y+1, levels, dataSize);
+       float *d = _face_getIFCo(f, lvl, S, x+0, y+1, levels, dataSize);
+       float a_cX = c[0]-a[0], a_cY = c[1]-a[1], a_cZ = c[2]-a[2];
+       float b_dX = d[0]-b[0], b_dY = d[1]-b[1], b_dZ = d[2]-b[2];
+       float length;
+
+       no[0] = b_dY*a_cZ - b_dZ*a_cY;
+       no[1] = b_dZ*a_cX - b_dX*a_cZ;
+       no[2] = b_dX*a_cY - b_dY*a_cX;
+
+       length = sqrt(no[0]*no[0] + no[1]*no[1] + no[2]*no[2]);
+
+       if (length>FLT_EPSILON) {
+               float invLength = 1.f/length;
+
+               no[0] *= invLength;
+               no[1] *= invLength;
+               no[2] *= invLength;
+       } else {
+               NormZero(no);
+       }
+}
 
 static void _face_free(CCGFace *f, CCGSubSurf *ss) {
        CCGSUBSURF_free(ss, f);
@@ -582,7 +617,7 @@ static void _face_unlinkMarkAndFree(CCGFace *f, CCGSubSurf *ss) {
 
 /***/
 
-CCGSubSurf *ccgSubSurf_new(CCGMeshIFC *ifc, CCGMeshHDL meshData, int subdivLevels, CCGAllocatorIFC *allocatorIFC, CCGAllocatorHDL allocator) {
+CCGSubSurf *ccgSubSurf_new(CCGMeshIFC *ifc, int subdivLevels, CCGAllocatorIFC *allocatorIFC, CCGAllocatorHDL allocator) {
        if (!allocatorIFC) {
                allocatorIFC = _getStandardAllocatorIFC();
                allocator = NULL;
@@ -601,12 +636,12 @@ CCGSubSurf *ccgSubSurf_new(CCGMeshIFC *ifc, CCGMeshHDL meshData, int subdivLevel
                ss->fMap = _ehash_new(0, &ss->allocatorIFC, ss->allocator);
 
                ss->meshIFC = *ifc;
-               ss->meshData = meshData;
-
+               
                ss->subdivLevels = subdivLevels;
                ss->numGrids = 0;
                ss->allowEdgeCreation = 0;
                ss->defaultCreaseValue = 0;
+               ss->defaultEdgeUserData = NULL;
 
                ss->useAgeCounts = 0;
                ss->vertUserAgeOffset = ss->edgeUserAgeOffset = ss->faceUserAgeOffset = 0;
@@ -645,6 +680,7 @@ void ccgSubSurf_free(CCGSubSurf *ss) {
 
        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);
@@ -657,12 +693,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) {
@@ -681,6 +735,15 @@ CCGError ccgSubSurf_setSubdivisionLevels(CCGSubSurf *ss, int subdivisionLevels)
        return eCCGError_None;
 }
 
+void ccgSubSurf_getUseAgeCounts(CCGSubSurf *ss, int *useAgeCounts_r, int *vertUserOffset_r, int *edgeUserOffset_r, int *faceUserOffset_r)
+{
+       *useAgeCounts_r = ss->useAgeCounts;
+
+       if (vertUserOffset_r) *vertUserOffset_r = ss->vertUserAgeOffset;
+       if (edgeUserOffset_r) *edgeUserOffset_r = ss->edgeUserAgeOffset;
+       if (faceUserOffset_r) *faceUserOffset_r = ss->faceUserAgeOffset;
+}
+
 CCGError ccgSubSurf_setUseAgeCounts(CCGSubSurf *ss, int useAgeCounts, int vertUserOffset, int edgeUserOffset, int faceUserOffset) {
        if (useAgeCounts) {
                if (    (vertUserOffset+4>ss->meshIFC.vertUserSize) ||
@@ -730,9 +793,9 @@ CCGError ccgSubSurf_initFullSync(CCGSubSurf *ss) {
        ss->oldEMap = ss->eMap; 
        ss->oldFMap = ss->fMap;
 
-       ss->vMap = _ehash_new(ss->oldVMap->numEntries, &ss->allocatorIFC, ss->allocator);
-       ss->eMap = _ehash_new(ss->oldFMap->numEntries, &ss->allocatorIFC, ss->allocator);
-       ss->fMap = _ehash_new(ss->oldEMap->numEntries, &ss->allocatorIFC, ss->allocator);
+       ss->vMap = _ehash_new(0, &ss->allocatorIFC, ss->allocator);
+       ss->eMap = _ehash_new(0, &ss->allocatorIFC, ss->allocator);
+       ss->fMap = _ehash_new(0, &ss->allocatorIFC, ss->allocator);
 
        ss->numGrids = 0;
 
@@ -811,9 +874,10 @@ 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);
@@ -821,12 +885,12 @@ CCGError ccgSubSurf_syncVert(CCGSubSurf *ss, CCGVertHDL vHDL, void *vertData) {
                        v = _vert_new(vHDL, ss->subdivLevels, ss->meshIFC.vertDataSize, 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];
@@ -850,12 +914,12 @@ CCGError ccgSubSurf_syncVert(CCGSubSurf *ss, CCGVertHDL vHDL, void *vertData) {
                        v = _vert_new(vHDL, ss->subdivLevels, ss->meshIFC.vertDataSize, 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);
@@ -863,12 +927,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);
@@ -916,12 +981,13 @@ 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) {
@@ -977,15 +1043,25 @@ CCGError ccgSubSurf_syncFace(CCGSubSurf *ss, CCGFaceHDL fHDL, int numVerts, CCGV
 
                for (k=0; k<numVerts; k++) {
                        ss->tempVerts[k] = _ehash_lookup(ss->vMap, vHDLs[k]);
+
+                       if (!ss->tempVerts[k])
+                               return eCCGError_InvalidValue;
                }
                for (k=0; k<numVerts; k++) {
                        ss->tempEdges[k] = _vert_findEdgeTo(ss->tempVerts[k], ss->tempVerts[(k+1)%numVerts]);
 
-                       if (ss->allowEdgeCreation && !ss->tempEdges[k]) {
-                               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);
-                               _ehash_insert(ss->eMap, (EHEntry*) e);
-                               e->v0->flags |= Vert_eEffected;
-                               e->v1->flags |= Vert_eEffected;
+                       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);
+                                       _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;
+                               }
                        }
                }
 
@@ -1019,6 +1095,7 @@ CCGError ccgSubSurf_syncFace(CCGSubSurf *ss, CCGFaceHDL fHDL, int numVerts, CCGV
                }
        }
 
+       if (f_r) *f_r = f;
        return eCCGError_None;
 }
 
@@ -1114,7 +1191,7 @@ static void ccgSubSurf__sync(CCGSubSurf *ss) {
        for (ptrIdx=0; ptrIdx<numEffectedE; ptrIdx++) {
                CCGEdge *e = effectedE[ptrIdx];
                void *co = EDGE_getCo(e, nextLvl, 1);
-               float sharpness = EDGE_getSharpness(e, curLvl, ss);
+               float sharpness = EDGE_getSharpness(e, curLvl);
 
                if (_edge_isBoundary(e) || sharpness>=1.0) {
                        VertDataCopy(co, VERT_getCo(e->v0, curLvl));
@@ -1141,7 +1218,7 @@ static void ccgSubSurf__sync(CCGSubSurf *ss) {
                        VertDataAdd(co, r);
                }
 
-               e->flags = 0;
+               // edge flags cleared later
        }
        for (ptrIdx=0; ptrIdx<numEffectedV; ptrIdx++) {
                CCGVert *v = effectedV[ptrIdx];
@@ -1149,10 +1226,14 @@ static void ccgSubSurf__sync(CCGSubSurf *ss) {
                void *nCo = VERT_getCo(v, nextLvl);
                int sharpCount = 0, allSharp = 1;
                float avgSharpness = 0.0;
+               int seam = VERT_seam(v, ss), seamEdges = 0;
 
                for (i=0; i<v->numEdges; i++) {
                        CCGEdge *e = v->edges[i];
-                       float sharpness = EDGE_getSharpness(e, curLvl, ss);
+                       float sharpness = EDGE_getSharpness(e, curLvl);
+
+                       if (seam && _edge_isBoundary(e))
+                               seamEdges++;
 
                        if (sharpness!=0.0f) {
                                sharpCount++;
@@ -1167,6 +1248,9 @@ static void ccgSubSurf__sync(CCGSubSurf *ss) {
                        avgSharpness = 1.0;
                }
 
+               if (seam && seamEdges < 2)
+                       seam = 0;
+
                if (!v->numEdges) {
                        VertDataCopy(nCo, co);
                } else if (_vert_isBoundary(v)) {
@@ -1209,14 +1293,25 @@ static void ccgSubSurf__sync(CCGSubSurf *ss) {
                        VertDataMulN(nCo, 1.0f/numEdges);
                }
 
-               if (sharpCount>1) {
+               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, ss);
+                               float sharpness = EDGE_getSharpness(e, curLvl);
 
-                               if (sharpness != 0.0) {
+                               if (seam) {
+                                       if (_edge_isBoundary(e)) {
+                                               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));
                                }
@@ -1244,7 +1339,7 @@ static void ccgSubSurf__sync(CCGSubSurf *ss) {
                        VertDataAdd(nCo, r);
                }
 
-               v->flags = 0;
+               // vert flags cleared later
        }
 
        if (ss->useAgeCounts) {
@@ -1374,7 +1469,7 @@ static void ccgSubSurf__sync(CCGSubSurf *ss) {
                         */
                for (ptrIdx=0; ptrIdx<numEffectedE; ptrIdx++) {
                        CCGEdge *e = (CCGEdge*) effectedE[ptrIdx];
-                       float sharpness = EDGE_getSharpness(e, curLvl, ss);
+                       float sharpness = EDGE_getSharpness(e, curLvl);
 
                        if (_edge_isBoundary(e) || sharpness>1.0) {
                                for (x=0; x<edgeSize-1; x++) {
@@ -1429,10 +1524,14 @@ static void ccgSubSurf__sync(CCGSubSurf *ss) {
                        void *nCo = VERT_getCo(v, nextLvl);
                        int sharpCount = 0, allSharp = 1;
                        float avgSharpness = 0.0;
+                       int seam = VERT_seam(v, ss), seamEdges = 0;
 
                        for (i=0; i<v->numEdges; i++) {
                                CCGEdge *e = v->edges[i];
-                               float sharpness = EDGE_getSharpness(e, curLvl, ss);
+                               float sharpness = EDGE_getSharpness(e, curLvl);
+
+                               if (seam && _edge_isBoundary(e))
+                                       seamEdges++;
 
                                if (sharpness!=0.0f) {
                                        sharpCount++;
@@ -1447,6 +1546,9 @@ static void ccgSubSurf__sync(CCGSubSurf *ss) {
                                avgSharpness = 1.0;
                        }
 
+                       if (seam && seamEdges < 2)
+                               seam = 0;
+
                        if (!v->numEdges) {
                                VertDataCopy(nCo, co);
                        } else if (_vert_isBoundary(v)) {
@@ -1491,14 +1593,23 @@ static void ccgSubSurf__sync(CCGSubSurf *ss) {
                                VertDataMulN(nCo, 1.0f/numEdges);
                        }
 
-                       if (sharpCount>1) {
+                       if ((sharpCount>1 && v->numFaces) || 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, ss);
+                                       float sharpness = EDGE_getSharpness(e, curLvl);
 
-                                       if (sharpness != 0.0) {
+                                       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));
                                        }
                                }
@@ -1533,7 +1644,7 @@ static void ccgSubSurf__sync(CCGSubSurf *ss) {
                         */
                for (ptrIdx=0; ptrIdx<numEffectedE; ptrIdx++) {
                        CCGEdge *e = (CCGEdge*) effectedE[ptrIdx];
-                       float sharpness = EDGE_getSharpness(e, curLvl, ss);
+                       float sharpness = EDGE_getSharpness(e, curLvl);
                        int sharpCount = 0;
                        float avgSharpness = 0.0;
 
@@ -1549,7 +1660,7 @@ static void ccgSubSurf__sync(CCGSubSurf *ss) {
                                avgSharpness = 0;
                        }
 
-                       if (_edge_isBoundary(e) && sharpCount<2) {
+                       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);
@@ -1687,8 +1798,6 @@ static void ccgSubSurf__sync(CCGSubSurf *ss) {
                        }
                }
 
-
-
                        /* copy down */
                edgeSize = 1 + (1<<(nextLvl));
                gridSize = 1 + (1<<((nextLvl)-1));
@@ -1722,9 +1831,8 @@ static void ccgSubSurf__sync(CCGSubSurf *ss) {
                }
        }
 
-#define FACE_getIFNo(f, lvl, S, x, y)  _face_getIFNo(f, lvl, S, x, y, subdivLevels, vertDataSize, normalDataOffset)
-#define AddNo(a, b)            {a[0]+= b[0], a[1]+= b[1], a[2] += b[2];}
-#define CopyNo(a, b)   {a[0] = b[0], a[1] = b[1], a[2]  = b[2];}
+#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);
@@ -1736,99 +1844,127 @@ static void ccgSubSurf__sync(CCGSubSurf *ss) {
                        int S, x, y;
 
                        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);
-                                               no[0] = no[1] = no[2] = 0.0;
-                                       }
-                               }
+                               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 (ptrIdx=0; ptrIdx<numEffectedF; ptrIdx++) {
                        CCGFace *f = (CCGFace*) effectedF[ptrIdx];
                        int S, x, y;
+                       float no[3];
 
                        for (S=0; S<f->numVerts; S++) {
-                               for (y=0; y<gridSize-1; y++) {
-                                       for (x=0; x<gridSize-1; x++) {
-                                               float *a = FACE_getIFCo(f, lvl, S, x+0, y+0);
-                                               float *b = FACE_getIFCo(f, lvl, S, x+1, y+0);
-                                               float *c = FACE_getIFCo(f, lvl, S, x+1, y+1);
-                                               float *d = FACE_getIFCo(f, lvl, S, x+0, y+1);
-                                               float a_cX = c[0]-a[0], a_cY = c[1]-a[1], a_cZ = c[2]-a[2];
-                                               float b_dX = d[0]-b[0], b_dY = d[1]-b[1], b_dZ = d[2]-b[2];
-                                               float no[3];
-
-                                               no[0] = b_dY*a_cZ - b_dZ*a_cY;
-                                               no[1] = b_dZ*a_cX - b_dX*a_cZ;
-                                               no[2] = b_dX*a_cY - b_dY*a_cX;
-
-                                               AddNo(FACE_getIFNo(f, lvl, S, x+0, y+0), no);
-                                               AddNo(FACE_getIFNo(f, lvl, S, x+1, y+0), no);
-                                               AddNo(FACE_getIFNo(f, lvl, S, x+1, y+1), no);
-                                               AddNo(FACE_getIFNo(f, lvl, S, x+0, y+1), no);
+                               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;
 
-                                                       AddNo(FACE_getIFNo(f, lvl, (S+1)%f->numVerts, 0, 1), no);
-                                                       AddNo(FACE_getIFNo(f, lvl, (S-1+f->numVerts)%f->numVerts, 1, 0), no);
+                                                       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) {
-                                                                       AddNo(FACE_getIFNo(f, lvl, K, 0, 0), no);
+                                                                       NormAdd(FACE_getIFNo(f, lvl, K, 0, 0), no);
                                                                }
                                                        }
                                                } else if (y==0) {
-                                                       AddNo(FACE_getIFNo(f, lvl, (S+1)%f->numVerts, 0, x), no);
-                                                       AddNo(FACE_getIFNo(f, lvl, (S+1)%f->numVerts, 0, x+1), no);
+                                                       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) {
-                                                       AddNo(FACE_getIFNo(f, lvl, (S-1+f->numVerts)%f->numVerts, y, 0), no);
-                                                       AddNo(FACE_getIFNo(f, lvl, (S-1+f->numVerts)%f->numVerts, y+1, 0), no);
+                                                       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 no[3] = {0};
+                       float length, *no = _vert_getNo(v, lvl, vertDataSize, normalDataOffset);
+
+                       NormZero(no);
 
                        for (i=0; i<v->numFaces; i++) {
                                CCGFace *f = v->faces[i];
-                               AddNo(no, FACE_getIFNo(f, lvl, _face_getVertIndex(f,v), gridSize-1, gridSize-1));
+                               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>FLT_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];
-                               CopyNo(FACE_getIFNo(f, lvl, _face_getVertIndex(f,v), gridSize-1, gridSize-1), no);
+                               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];
-                       int x;
 
-                       for (x=0; x<edgeSize; x++) {
-                               float *no = _edge_getNo(e, lvl, x, vertDataSize, normalDataOffset);
-                               no[0] = no[1] = no[2] = 0.0;
-                       }
+                       if (e->numFaces) {
+                               CCGFace *fLast = e->faces[e->numFaces-1];
+                               int x;
 
-                       for (i=0; i<e->numFaces; i++) {
-                               CCGFace *f = e->faces[i];
+                               for (i=0; i<e->numFaces-1; i++) {
+                                       CCGFace *f = e->faces[i];
 
-                               for (x=1; x<edgeSize-1; x++) {
-                                       AddNo(  _edge_getNo(e, lvl, x, vertDataSize, normalDataOffset),
-                                                       _face_getIFNoEdge(f, e, lvl, x, 0, subdivLevels, vertDataSize, normalDataOffset));
+                                       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; i++) {
-                               CCGFace *f = e->faces[i];
+                               for (i=0; i<e->numFaces-1; i++) {
+                                       CCGFace *f = e->faces[i];
 
-                               for (x=1; x<edgeSize-1; x++) {
-                                       CopyNo( _face_getIFNoEdge(f, e, lvl, x, 0, subdivLevels, vertDataSize, normalDataOffset),
-                                                       _edge_getNo(e, lvl, x, vertDataSize, normalDataOffset));
+                                       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));
+                                       }
                                }
                        }
                }
@@ -1837,8 +1973,8 @@ static void ccgSubSurf__sync(CCGSubSurf *ss) {
                        int S;
 
                        for (S=0; S<f->numVerts; S++) {
-                               CopyNo( FACE_getIFNo(f, lvl, (S+1)%f->numVerts, 0, gridSize-1),
-                                               FACE_getIFNo(f, lvl, S, gridSize-1, 0));
+                               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++) {
@@ -1857,17 +1993,24 @@ static void ccgSubSurf__sync(CCGSubSurf *ss) {
                                                        no[1] *= invLength;
                                                        no[2] *= invLength;
                                                } else {
-                                                       no[0] = no[1] = no[2] = 0.0;
+                                                       NormZero(no);
                                                }
                                        }
                                }
                        }
                }
        }
-#undef CopyNo
-#undef AddNo
 #undef FACE_getIFNo
 
+       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;
+       }
+
 #undef VERT_getCo
 #undef EDGE_getCo
 #undef FACE_getIECo
@@ -2016,6 +2159,9 @@ void *ccgSubSurf_getEdgeLevelData(CCGSubSurf *ss, CCGEdge *e, int x, int level)
                return _edge_getCo(e, level, x, ss->meshIFC.vertDataSize);
        }
 }
+float ccgSubSurf_getEdgeCrease(CCGSubSurf *ss, CCGEdge *e) {
+       return e->crease;
+}
 
 /* Face accessors */
 
@@ -2051,6 +2197,15 @@ CCGEdge *ccgSubSurf_getFaceEdge(CCGSubSurf *ss, CCGFace *f, int index) {
                return FACE_getEdges(f)[index];
        }
 }
+int ccgSubSurf_getFaceEdgeIndex(CCGSubSurf *ss, 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(CCGSubSurf *ss, CCGFace *f) {
        return FACE_getCenterData(f);
 }