text: /*
   * This program is free software; you can redistribute it and/or
   * modify it under the terms of the GNU General Public License
   * as published by the Free Software Foundation; either version 2
   * of the License, or (at your option) any later version.
   *
   * This program is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU General Public License for more details.
   *
   * You should have received a copy of the GNU General Public License
   * along with this program; if not, write to the Free Software Foundation,
   * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
   */
  
  /** \file
   * \ingroup bke
   */
  
  #ifndef __CCGSUBSURF_INLINE_H__
  #define __CCGSUBSURF_INLINE_H__
  
  BLI_INLINE int ccg_gridsize(int level)
  {
    BLI_assert(level > 0);
    BLI_assert(level <= CCGSUBSURF_LEVEL_MAX + 1);
    return (1 << (level - 1)) + 1;
  }
  
  BLI_INLINE int ccg_edgesize(int level)
  {
    BLI_assert(level > 0);
    BLI_assert(level <= CCGSUBSURF_LEVEL_MAX + 1);
    return 1 + (1 << level);
  }
  
  BLI_INLINE int ccg_spacing(int high_level, int low_level)
  {
    BLI_assert(high_level > 0 && low_level > 0);
    BLI_assert(high_level >= low_level);
    BLI_assert((high_level - low_level) <= CCGSUBSURF_LEVEL_MAX);
    return 1 << (high_level - low_level);
  }
  
  BLI_INLINE int ccg_edgebase(int level)
  {
    BLI_assert(level > 0);
    BLI_assert(level <= CCGSUBSURF_LEVEL_MAX + 1);
    return level + (1 << level) - 1;
  }
  
  /* **** */
  
  BLI_INLINE byte *VERT_getLevelData(CCGVert *v)
  {
    return (byte *)(&(v)[1]);
  }
  
  BLI_INLINE byte *EDGE_getLevelData(CCGEdge *e)
  {
    return (byte *)(&(e)[1]);
  }
  
  BLI_INLINE CCGVert **FACE_getVerts(CCGFace *f)
  {
    return (CCGVert **)(&f[1]);
  }
  
  BLI_INLINE CCGEdge **FACE_getEdges(CCGFace *f)
  {
    return (CCGEdge **)(&(FACE_getVerts(f)[f->numVerts]));
  }
  
  BLI_INLINE byte *FACE_getCenterData(CCGFace *f)
  {
    return (byte *)(&(FACE_getEdges(f)[(f)->numVerts]));
  }
  
  /* **** */
  
  BLI_INLINE void *ccg_vert_getCo(CCGVert *v, int lvl, int dataSize)
  {
    return &VERT_getLevelData(v)[lvl * dataSize];
  }
  
  BLI_INLINE float *ccg_vert_getNo(CCGVert *v, int lvl, int dataSize, int normalDataOffset)
  {
    return (float *)&VERT_getLevelData(v)[lvl * dataSize + normalDataOffset];
  }
  
  BLI_INLINE void *ccg_edge_getCo(CCGEdge *e, int lvl, int x, int dataSize)
  {
    int levelBase = ccg_edgebase(lvl);
    return &EDGE_getLevelData(e)[dataSize * (levelBase + x)];
  }
  
  BLI_INLINE float *ccg_edge_getNo(CCGEdge *e, int lvl, int x, int dataSize, int normalDataOffset)
  {
    int levelBase = ccg_edgebase(lvl);
    return (float *)&EDGE_getLevelData(e)[dataSize * (levelBase + x) + normalDataOffset];
  }
  
  BLI_INLINE void *ccg_face_getIECo(CCGFace *f, int lvl, int S, int x, int levels, int dataSize)
  {
    int maxGridSize = ccg_gridsize(levels);
    int spacing = ccg_spacing(levels, lvl);
    byte *gridBase = FACE_getCenterData(f) +
                     dataSize * (1 + S * (maxGridSize + maxGridSize * maxGridSize));
    return &gridBase[dataSize * x * spacing];
  }
  
  BLI_INLINE void *ccg_face_getIENo(
      CCGFace *f, int lvl, int S, int x, int levels, int dataSize, int normalDataOffset)
  {
    int maxGridSize = ccg_gridsize(levels);
    int spacing = ccg_spacing(levels, lvl);
    byte *gridBase = FACE_getCenterData(f) +
                     dataSize * (1 + S * (maxGridSize + maxGridSize * maxGridSize));
    return &gridBase[dataSize * x * spacing + normalDataOffset];
  }
  
  BLI_INLINE void *ccg_face_getIFCo(
      CCGFace *f, int lvl, int S, int x, int y, int levels, int dataSize)
  {
    int maxGridSize = ccg_gridsize(levels);
    int spacing = ccg_spacing(levels, lvl);
    byte *gridBase = FACE_getCenterData(f) +
                     dataSize * (1 + S * (maxGridSize + maxGridSize * maxGridSize));
    return &gridBase[dataSize * (maxGridSize + (y * maxGridSize + x) * spacing)];
  }
  
  BLI_INLINE float *ccg_face_getIFNo(
      CCGFace *f, int lvl, int S, int x, int y, int levels, int dataSize, int normalDataOffset)
  {
    int maxGridSize = ccg_gridsize(levels);
    int spacing = ccg_spacing(levels, lvl);
    byte *gridBase = FACE_getCenterData(f) +
                     dataSize * (1 + S * (maxGridSize + maxGridSize * maxGridSize));
    return (float *)&gridBase[dataSize * (maxGridSize + (y * maxGridSize + x) * spacing) +
                              normalDataOffset];
  }
  
  BLI_INLINE int ccg_face_getVertIndex(CCGFace *f, CCGVert *v)
  {
    int i;
    for (i = 0; i < f->numVerts; i++)
      if (FACE_getVerts(f)[i] == v)
        return i;
    return -1;
  }
  
  BLI_INLINE int ccg_face_getEdgeIndex(CCGFace *f, CCGEdge *e)
  {
    int i;
    for (i = 0; i < f->numVerts; i++)
      if (FACE_getEdges(f)[i] == e)
        return i;
    return -1;
  }
  
  BLI_INLINE void *ccg_face_getIFCoEdge(
      CCGFace *f, CCGEdge *e, int f_ed_idx, int lvl, int eX, int eY, int levels, int dataSize)
  {
    int maxGridSize = ccg_gridsize(levels);
    int spacing = ccg_spacing(levels, lvl);
    int x, y, cx, cy;
  
    BLI_assert(f_ed_idx == ccg_face_getEdgeIndex(f, e));
  
    eX = eX * spacing;
    eY = eY * spacing;
    if (e->v0 != FACE_getVerts(f)[f_ed_idx]) {
      eX = (maxGridSize * 2 - 1) - 1 - eX;
    }
    y = maxGridSize - 1 - eX;
    x = maxGridSize - 1 - eY;
    if (x < 0) {
      f_ed_idx = (f_ed_idx + f->numVerts - 1) % f->numVerts;
      cx = y;
      cy = -x;
    }
    else if (y < 0) {
      f_ed_idx = (f_ed_idx + 1) % f->numVerts;
      cx = -y;
      cy = x;
    }
    else {
      cx = x;
      cy = y;
    }
    return ccg_face_getIFCo(f, levels, f_ed_idx, cx, cy, levels, dataSize);
  }
  
  BLI_INLINE void Normalize(float no[3])
  {
    const float length = sqrtf(no[0] * no[0] + no[1] * no[1] + no[2] * no[2]);
  
    if (length > EPSILON) {
      const float length_inv = 1.0f / length;
  
      no[0] *= length_inv;
      no[1] *= length_inv;
      no[2] *= length_inv;
    }
    else {
      NormZero(no);
    }
  }
  
  /* Data layers mathematics. */
  
  BLI_INLINE int VertDataEqual(const float a[], const float b[], const CCGSubSurf *ss)
  {
    int i;
    for (i = 0; i < ss->meshIFC.numLayers; i++) {
      if (a[i] != b[i])
        return 0;
    }
    return 1;
  }
  
  BLI_INLINE void VertDataZero(float v[], const CCGSubSurf *ss)
  {
    memset(v, 0, sizeof(float) * ss->meshIFC.numLayers);
  }
  
  BLI_INLINE void VertDataCopy(float dst[], const float src[], const CCGSubSurf *ss)
  {
    int i;
    for (i = 0; i < ss->meshIFC.numLayers; i++)
      dst[i] = src[i];
  }
  
  BLI_INLINE void VertDataAdd(float a[], const float b[], const CCGSubSurf *ss)
  {
    int i;
    for (i = 0; i < ss->meshIFC.numLayers; i++)
      a[i] += b[i];
  }
  
  BLI_INLINE void VertDataSub(float a[], const float b[], const CCGSubSurf *ss)
  {
    int i;
    for (i = 0; i < ss->meshIFC.numLayers; i++)
      a[i] -= b[i];
  }
  
  BLI_INLINE void VertDataMulN(float v[], float f, const CCGSubSurf *ss)
  {
    int i;
    for (i = 0; i < ss->meshIFC.numLayers; i++)
      v[i] *= f;
  }
  
  BLI_INLINE void VertDataAvg4(float v[],
                               const float a[],
                               const float b[],
                               const float c[],
                               const float d[],
                               const CCGSubSurf *ss)
  {
    int i;
    for (i = 0; i < ss->meshIFC.numLayers; i++)
      v[i] = (a[i] + b[i] + c[i] + d[i]) * 0.25f;
  }
  
  #endif /* __CCGSUBSURF_INLINE_H__ */
  