A lot of files were missing copyright field in the header and
the Blender Foundation contributed to them in a sense of bug
fixing and general maintenance.
This change makes it explicit that those files are at least
partially copyrighted by the Blender Foundation.
Note that this does not make it so the Blender Foundation is
the only holder of the copyright in those files, and developers
who do not have a signed contract with the foundation still
hold the copyright as well.
Another aspect of this change is using SPDX format for the
header. We already used it for the license specification,
and now we state it for the copyright as well, following the
FAQ:
https://reuse.software/faq/
1368 lines
41 KiB
C
1368 lines
41 KiB
C
/* SPDX-FileCopyrightText: 2023 Blender Foundation
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
/** \file
|
|
* \ingroup bke
|
|
*/
|
|
|
|
#include "BLI_sys_types.h" /* for intptr_t support */
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
#include "BLI_math.h"
|
|
#include "BLI_task.h"
|
|
#include "BLI_utildefines.h" /* for BLI_assert */
|
|
|
|
#include "CCGSubSurf.h"
|
|
#include "CCGSubSurf_intern.h"
|
|
|
|
#define FACE_calcIFNo(f, lvl, S, x, y, no) \
|
|
_face_calcIFNo(f, lvl, S, x, y, no, subdivLevels, vertDataSize)
|
|
|
|
/* TODO(sergey): Deduplicate the following functions/ */
|
|
static void *_edge_getCoVert(CCGEdge *e, CCGVert *v, int lvl, int x, int dataSize)
|
|
{
|
|
int levelBase = ccg_edgebase(lvl);
|
|
if (v == e->v0) {
|
|
return &EDGE_getLevelData(e)[dataSize * (levelBase + x)];
|
|
}
|
|
return &EDGE_getLevelData(e)[dataSize * (levelBase + (1 << lvl) - x)];
|
|
}
|
|
/* *************************************************** */
|
|
|
|
static int _edge_isBoundary(const CCGEdge *e)
|
|
{
|
|
return e->numFaces < 2;
|
|
}
|
|
|
|
static bool _vert_isBoundary(const CCGVert *v)
|
|
{
|
|
for (int i = 0; i < v->numEdges; i++) {
|
|
if (_edge_isBoundary(v->edges[i])) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static CCGVert *_edge_getOtherVert(CCGEdge *e, CCGVert *vQ)
|
|
{
|
|
if (vQ == e->v0) {
|
|
return e->v1;
|
|
}
|
|
|
|
return e->v0;
|
|
}
|
|
|
|
static float *_face_getIFNoEdge(CCGFace *f,
|
|
CCGEdge *e,
|
|
int f_ed_idx,
|
|
int lvl,
|
|
int eX,
|
|
int eY,
|
|
int levels,
|
|
int dataSize,
|
|
int normalDataOffset)
|
|
{
|
|
return (float *)((byte *)ccg_face_getIFCoEdge(f, e, f_ed_idx, lvl, eX, eY, levels, dataSize) +
|
|
normalDataOffset);
|
|
}
|
|
|
|
static void _face_calcIFNo(
|
|
CCGFace *f, int lvl, int S, int x, int y, float no[3], int levels, int dataSize)
|
|
{
|
|
float *a = ccg_face_getIFCo(f, lvl, S, x + 0, y + 0, levels, dataSize);
|
|
float *b = ccg_face_getIFCo(f, lvl, S, x + 1, y + 0, levels, dataSize);
|
|
float *c = ccg_face_getIFCo(f, lvl, S, x + 1, y + 1, levels, dataSize);
|
|
float *d = ccg_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];
|
|
|
|
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;
|
|
|
|
Normalize(no);
|
|
}
|
|
|
|
static int VERT_seam(const CCGVert *v)
|
|
{
|
|
return ((v->flags & Vert_eSeam) != 0);
|
|
}
|
|
|
|
static float EDGE_getSharpness(CCGEdge *e, int lvl)
|
|
{
|
|
if (!lvl) {
|
|
return e->crease;
|
|
}
|
|
if (!e->crease) {
|
|
return 0.0f;
|
|
}
|
|
if (e->crease - lvl < 0.0f) {
|
|
return 0.0f;
|
|
}
|
|
return e->crease - lvl;
|
|
}
|
|
|
|
typedef struct CCGSubSurfCalcSubdivData {
|
|
CCGSubSurf *ss;
|
|
CCGVert **effectedV;
|
|
CCGEdge **effectedE;
|
|
CCGFace **effectedF;
|
|
int numEffectedV;
|
|
int numEffectedE;
|
|
int numEffectedF;
|
|
|
|
int curLvl;
|
|
} CCGSubSurfCalcSubdivData;
|
|
|
|
static void ccgSubSurf__calcVertNormals_faces_accumulate_cb(
|
|
void *__restrict userdata, const int ptrIdx, const TaskParallelTLS *__restrict UNUSED(tls))
|
|
{
|
|
CCGSubSurfCalcSubdivData *data = userdata;
|
|
|
|
CCGSubSurf *ss = data->ss;
|
|
CCGFace *f = data->effectedF[ptrIdx];
|
|
|
|
const int subdivLevels = ss->subdivLevels;
|
|
const int lvl = ss->subdivLevels;
|
|
const int gridSize = ccg_gridsize(lvl);
|
|
const int normalDataOffset = ss->normalDataOffset;
|
|
const int vertDataSize = ss->meshIFC.vertDataSize;
|
|
|
|
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++) {
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void ccgSubSurf__calcVertNormals_faces_finalize_cb(
|
|
void *__restrict userdata, const int ptrIdx, const TaskParallelTLS *__restrict UNUSED(tls))
|
|
{
|
|
CCGSubSurfCalcSubdivData *data = userdata;
|
|
|
|
CCGSubSurf *ss = data->ss;
|
|
CCGFace *f = data->effectedF[ptrIdx];
|
|
|
|
const int subdivLevels = ss->subdivLevels;
|
|
const int lvl = ss->subdivLevels;
|
|
const int gridSize = ccg_gridsize(lvl);
|
|
const int normalDataOffset = ss->normalDataOffset;
|
|
const int vertDataSize = ss->meshIFC.vertDataSize;
|
|
|
|
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);
|
|
Normalize(no);
|
|
}
|
|
}
|
|
|
|
VertDataCopy((float *)((byte *)FACE_getCenterData(f) + normalDataOffset),
|
|
FACE_getIFNo(f, lvl, S, 0, 0),
|
|
ss);
|
|
|
|
for (x = 1; x < gridSize - 1; x++) {
|
|
NormCopy(FACE_getIENo(f, lvl, S, x), FACE_getIFNo(f, lvl, S, x, 0));
|
|
}
|
|
}
|
|
}
|
|
|
|
static void ccgSubSurf__calcVertNormals_edges_accumulate_cb(
|
|
void *__restrict userdata, const int ptrIdx, const TaskParallelTLS *__restrict UNUSED(tls))
|
|
{
|
|
CCGSubSurfCalcSubdivData *data = userdata;
|
|
|
|
CCGSubSurf *ss = data->ss;
|
|
CCGEdge *e = data->effectedE[ptrIdx];
|
|
|
|
const int subdivLevels = ss->subdivLevels;
|
|
const int lvl = ss->subdivLevels;
|
|
const int edgeSize = ccg_edgesize(lvl);
|
|
const int normalDataOffset = ss->normalDataOffset;
|
|
const int vertDataSize = ss->meshIFC.vertDataSize;
|
|
|
|
if (e->numFaces) {
|
|
CCGFace *fLast = e->faces[e->numFaces - 1];
|
|
int x, i;
|
|
|
|
for (i = 0; i < e->numFaces - 1; i++) {
|
|
CCGFace *f = e->faces[i];
|
|
const int f_ed_idx = ccg_face_getEdgeIndex(f, e);
|
|
const int f_ed_idx_last = ccg_face_getEdgeIndex(fLast, e);
|
|
|
|
for (x = 1; x < edgeSize - 1; x++) {
|
|
NormAdd(
|
|
_face_getIFNoEdge(
|
|
fLast, e, f_ed_idx_last, lvl, x, 0, subdivLevels, vertDataSize, normalDataOffset),
|
|
_face_getIFNoEdge(
|
|
f, e, f_ed_idx, lvl, x, 0, subdivLevels, vertDataSize, normalDataOffset));
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < e->numFaces - 1; i++) {
|
|
CCGFace *f = e->faces[i];
|
|
const int f_ed_idx = ccg_face_getEdgeIndex(f, e);
|
|
const int f_ed_idx_last = ccg_face_getEdgeIndex(fLast, e);
|
|
|
|
for (x = 1; x < edgeSize - 1; x++) {
|
|
NormCopy(
|
|
_face_getIFNoEdge(
|
|
f, e, f_ed_idx, lvl, x, 0, subdivLevels, vertDataSize, normalDataOffset),
|
|
_face_getIFNoEdge(
|
|
fLast, e, f_ed_idx_last, lvl, x, 0, subdivLevels, vertDataSize, normalDataOffset));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void ccgSubSurf__calcVertNormals(CCGSubSurf *ss,
|
|
CCGVert **effectedV,
|
|
CCGEdge **effectedE,
|
|
CCGFace **effectedF,
|
|
int numEffectedV,
|
|
int numEffectedE,
|
|
int numEffectedF)
|
|
{
|
|
int i, ptrIdx;
|
|
const int subdivLevels = ss->subdivLevels;
|
|
const int lvl = ss->subdivLevels;
|
|
const int edgeSize = ccg_edgesize(lvl);
|
|
const int gridSize = ccg_gridsize(lvl);
|
|
const int normalDataOffset = ss->normalDataOffset;
|
|
const int vertDataSize = ss->meshIFC.vertDataSize;
|
|
|
|
CCGSubSurfCalcSubdivData data = {
|
|
.ss = ss,
|
|
.effectedV = effectedV,
|
|
.effectedE = effectedE,
|
|
.effectedF = effectedF,
|
|
.numEffectedV = numEffectedV,
|
|
.numEffectedE = numEffectedE,
|
|
.numEffectedF = numEffectedF,
|
|
};
|
|
|
|
{
|
|
TaskParallelSettings settings;
|
|
BLI_parallel_range_settings_defaults(&settings);
|
|
settings.min_iter_per_thread = CCG_TASK_LIMIT;
|
|
BLI_task_parallel_range(
|
|
0, numEffectedF, &data, ccgSubSurf__calcVertNormals_faces_accumulate_cb, &settings);
|
|
}
|
|
|
|
/* XXX can I reduce the number of normalization calls here? */
|
|
for (ptrIdx = 0; ptrIdx < numEffectedV; ptrIdx++) {
|
|
CCGVert *v = (CCGVert *)effectedV[ptrIdx];
|
|
float *no = VERT_getNo(v, lvl);
|
|
|
|
NormZero(no);
|
|
|
|
for (i = 0; i < v->numFaces; i++) {
|
|
CCGFace *f = v->faces[i];
|
|
NormAdd(no, FACE_getIFNo(f, lvl, ccg_face_getVertIndex(f, v), gridSize - 1, gridSize - 1));
|
|
}
|
|
|
|
if (UNLIKELY(v->numFaces == 0)) {
|
|
NormCopy(no, VERT_getCo(v, lvl));
|
|
}
|
|
|
|
Normalize(no);
|
|
|
|
for (i = 0; i < v->numFaces; i++) {
|
|
CCGFace *f = v->faces[i];
|
|
NormCopy(FACE_getIFNo(f, lvl, ccg_face_getVertIndex(f, v), gridSize - 1, gridSize - 1), no);
|
|
}
|
|
}
|
|
|
|
{
|
|
TaskParallelSettings settings;
|
|
BLI_parallel_range_settings_defaults(&settings);
|
|
settings.min_iter_per_thread = CCG_TASK_LIMIT;
|
|
BLI_task_parallel_range(
|
|
0, numEffectedE, &data, ccgSubSurf__calcVertNormals_edges_accumulate_cb, &settings);
|
|
}
|
|
|
|
{
|
|
TaskParallelSettings settings;
|
|
BLI_parallel_range_settings_defaults(&settings);
|
|
settings.min_iter_per_thread = CCG_TASK_LIMIT;
|
|
BLI_task_parallel_range(
|
|
0, numEffectedF, &data, ccgSubSurf__calcVertNormals_faces_finalize_cb, &settings);
|
|
}
|
|
|
|
for (ptrIdx = 0; ptrIdx < numEffectedE; ptrIdx++) {
|
|
CCGEdge *e = (CCGEdge *)effectedE[ptrIdx];
|
|
|
|
if (e->numFaces) {
|
|
CCGFace *f = e->faces[0];
|
|
int x;
|
|
const int f_ed_idx = ccg_face_getEdgeIndex(f, e);
|
|
|
|
for (x = 0; x < edgeSize; x++) {
|
|
NormCopy(EDGE_getNo(e, lvl, x),
|
|
_face_getIFNoEdge(
|
|
f, e, f_ed_idx, lvl, x, 0, subdivLevels, vertDataSize, normalDataOffset));
|
|
}
|
|
}
|
|
else {
|
|
/* set to zero here otherwise the normals are uninitialized memory
|
|
* render: tests/animation/knight.blend with valgrind.
|
|
* we could be more clever and interpolate vertex normals but these are
|
|
* most likely not used so just zero out. */
|
|
int x;
|
|
|
|
for (x = 0; x < edgeSize; x++) {
|
|
float *no = EDGE_getNo(e, lvl, x);
|
|
NormCopy(no, EDGE_getCo(e, lvl, x));
|
|
Normalize(no);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void ccgSubSurf__calcSubdivLevel_interior_faces_edges_midpoints_cb(
|
|
void *__restrict userdata, const int ptrIdx, const TaskParallelTLS *__restrict UNUSED(tls))
|
|
{
|
|
CCGSubSurfCalcSubdivData *data = userdata;
|
|
|
|
CCGSubSurf *ss = data->ss;
|
|
CCGFace *f = data->effectedF[ptrIdx];
|
|
|
|
const int subdivLevels = ss->subdivLevels;
|
|
const int curLvl = data->curLvl;
|
|
const int nextLvl = curLvl + 1;
|
|
const int gridSize = ccg_gridsize(curLvl);
|
|
const int vertDataSize = ss->meshIFC.vertDataSize;
|
|
|
|
int S, x, y;
|
|
|
|
/* interior face midpoints
|
|
* - 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;
|
|
const float *co0 = FACE_getIFCo(f, curLvl, S, x + 0, y + 0);
|
|
const float *co1 = FACE_getIFCo(f, curLvl, S, x + 1, y + 0);
|
|
const float *co2 = FACE_getIFCo(f, curLvl, S, x + 1, y + 1);
|
|
const float *co3 = FACE_getIFCo(f, curLvl, S, x + 0, y + 1);
|
|
float *co = FACE_getIFCo(f, nextLvl, S, fx, fy);
|
|
|
|
VertDataAvg4(co, co0, co1, co2, co3, ss);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* interior edge midpoints
|
|
* - old interior edge points
|
|
* - new interior face midpoints
|
|
*/
|
|
for (S = 0; S < f->numVerts; S++) {
|
|
for (x = 0; x < gridSize - 1; x++) {
|
|
int fx = x * 2 + 1;
|
|
const float *co0 = FACE_getIECo(f, curLvl, S, x + 0);
|
|
const float *co1 = FACE_getIECo(f, curLvl, S, x + 1);
|
|
const float *co2 = FACE_getIFCo(f, nextLvl, (S + 1) % f->numVerts, 1, fx);
|
|
const float *co3 = FACE_getIFCo(f, nextLvl, S, fx, 1);
|
|
float *co = FACE_getIECo(f, nextLvl, S, fx);
|
|
|
|
VertDataAvg4(co, co0, co1, co2, co3, ss);
|
|
}
|
|
|
|
/* interior face interior edge midpoints
|
|
* - old interior face points
|
|
* - 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;
|
|
const float *co0 = FACE_getIFCo(f, curLvl, S, x, y + 0);
|
|
const float *co1 = FACE_getIFCo(f, curLvl, S, x, y + 1);
|
|
const float *co2 = FACE_getIFCo(f, nextLvl, S, fx - 1, fy);
|
|
const float *co3 = FACE_getIFCo(f, nextLvl, S, fx + 1, fy);
|
|
float *co = FACE_getIFCo(f, nextLvl, S, fx, fy);
|
|
|
|
VertDataAvg4(co, co0, co1, co2, co3, ss);
|
|
}
|
|
}
|
|
|
|
/* horizontal */
|
|
for (y = 1; y < gridSize - 1; y++) {
|
|
for (x = 0; x < gridSize - 1; x++) {
|
|
int fx = x * 2 + 1;
|
|
int fy = y * 2;
|
|
const float *co0 = FACE_getIFCo(f, curLvl, S, x + 0, y);
|
|
const float *co1 = FACE_getIFCo(f, curLvl, S, x + 1, y);
|
|
const float *co2 = FACE_getIFCo(f, nextLvl, S, fx, fy - 1);
|
|
const float *co3 = FACE_getIFCo(f, nextLvl, S, fx, fy + 1);
|
|
float *co = FACE_getIFCo(f, nextLvl, S, fx, fy);
|
|
|
|
VertDataAvg4(co, co0, co1, co2, co3, ss);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void ccgSubSurf__calcSubdivLevel_interior_faces_edges_centerpoints_shift_cb(
|
|
void *__restrict userdata, const int ptrIdx, const TaskParallelTLS *__restrict UNUSED(tls))
|
|
{
|
|
CCGSubSurfCalcSubdivData *data = userdata;
|
|
|
|
CCGSubSurf *ss = data->ss;
|
|
CCGFace *f = data->effectedF[ptrIdx];
|
|
|
|
const int subdivLevels = ss->subdivLevels;
|
|
const int curLvl = data->curLvl;
|
|
const int nextLvl = curLvl + 1;
|
|
const int gridSize = ccg_gridsize(curLvl);
|
|
const int vertDataSize = ss->meshIFC.vertDataSize;
|
|
|
|
float *q_thread = alloca(vertDataSize);
|
|
float *r_thread = alloca(vertDataSize);
|
|
|
|
int S, x, y;
|
|
|
|
/* interior center point shift
|
|
* - old face center point (shifting)
|
|
* - old interior edge points
|
|
* - new interior face midpoints
|
|
*/
|
|
VertDataZero(q_thread, ss);
|
|
for (S = 0; S < f->numVerts; S++) {
|
|
VertDataAdd(q_thread, FACE_getIFCo(f, nextLvl, S, 1, 1), ss);
|
|
}
|
|
VertDataMulN(q_thread, 1.0f / f->numVerts, ss);
|
|
VertDataZero(r_thread, ss);
|
|
for (S = 0; S < f->numVerts; S++) {
|
|
VertDataAdd(r_thread, FACE_getIECo(f, curLvl, S, 1), ss);
|
|
}
|
|
VertDataMulN(r_thread, 1.0f / f->numVerts, ss);
|
|
|
|
VertDataMulN((float *)FACE_getCenterData(f), f->numVerts - 2.0f, ss);
|
|
VertDataAdd((float *)FACE_getCenterData(f), q_thread, ss);
|
|
VertDataAdd((float *)FACE_getCenterData(f), r_thread, ss);
|
|
VertDataMulN((float *)FACE_getCenterData(f), 1.0f / f->numVerts, ss);
|
|
|
|
for (S = 0; S < f->numVerts; S++) {
|
|
/* interior face shift
|
|
* - old interior face point (shifting)
|
|
* - new interior edge midpoints
|
|
* - 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;
|
|
const float *co = FACE_getIFCo(f, curLvl, S, x, y);
|
|
float *nCo = FACE_getIFCo(f, nextLvl, S, fx, fy);
|
|
|
|
VertDataAvg4(q_thread,
|
|
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),
|
|
ss);
|
|
|
|
VertDataAvg4(r_thread,
|
|
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),
|
|
ss);
|
|
|
|
VertDataCopy(nCo, co, ss);
|
|
VertDataSub(nCo, q_thread, ss);
|
|
VertDataMulN(nCo, 0.25f, ss);
|
|
VertDataAdd(nCo, r_thread, ss);
|
|
}
|
|
}
|
|
|
|
/* interior edge interior shift
|
|
* - old interior edge point (shifting)
|
|
* - new interior edge midpoints
|
|
* - new interior face midpoints
|
|
*/
|
|
for (x = 1; x < gridSize - 1; x++) {
|
|
int fx = x * 2;
|
|
const float *co = FACE_getIECo(f, curLvl, S, x);
|
|
float *nCo = FACE_getIECo(f, nextLvl, S, fx);
|
|
|
|
VertDataAvg4(q_thread,
|
|
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),
|
|
ss);
|
|
|
|
VertDataAvg4(r_thread,
|
|
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),
|
|
ss);
|
|
|
|
VertDataCopy(nCo, co, ss);
|
|
VertDataSub(nCo, q_thread, ss);
|
|
VertDataMulN(nCo, 0.25f, ss);
|
|
VertDataAdd(nCo, r_thread, ss);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void ccgSubSurf__calcSubdivLevel_verts_copydata_cb(
|
|
void *__restrict userdata, const int ptrIdx, const TaskParallelTLS *__restrict UNUSED(tls))
|
|
{
|
|
CCGSubSurfCalcSubdivData *data = userdata;
|
|
|
|
CCGSubSurf *ss = data->ss;
|
|
CCGFace *f = data->effectedF[ptrIdx];
|
|
|
|
const int subdivLevels = ss->subdivLevels;
|
|
const int nextLvl = data->curLvl + 1;
|
|
const int gridSize = ccg_gridsize(nextLvl);
|
|
const int cornerIdx = gridSize - 1;
|
|
const int vertDataSize = ss->meshIFC.vertDataSize;
|
|
|
|
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), (float *)FACE_getCenterData(f), ss);
|
|
VertDataCopy(FACE_getIECo(f, nextLvl, S, 0), (float *)FACE_getCenterData(f), ss);
|
|
VertDataCopy(FACE_getIFCo(f, nextLvl, S, cornerIdx, cornerIdx),
|
|
VERT_getCo(FACE_getVerts(f)[S], nextLvl),
|
|
ss);
|
|
VertDataCopy(FACE_getIECo(f, nextLvl, S, cornerIdx),
|
|
EDGE_getCo(FACE_getEdges(f)[S], nextLvl, cornerIdx),
|
|
ss);
|
|
for (x = 1; x < gridSize - 1; x++) {
|
|
float *co = FACE_getIECo(f, nextLvl, S, x);
|
|
VertDataCopy(FACE_getIFCo(f, nextLvl, S, x, 0), co, ss);
|
|
VertDataCopy(FACE_getIFCo(f, nextLvl, (S + 1) % f->numVerts, 0, x), co, ss);
|
|
}
|
|
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),
|
|
ss);
|
|
VertDataCopy(FACE_getIFCo(f, nextLvl, S, x, cornerIdx),
|
|
_edge_getCoVert(prevE, FACE_getVerts(f)[S], nextLvl, eI, vertDataSize),
|
|
ss);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void ccgSubSurf__calcSubdivLevel(CCGSubSurf *ss,
|
|
CCGVert **effectedV,
|
|
CCGEdge **effectedE,
|
|
CCGFace **effectedF,
|
|
const int numEffectedV,
|
|
const int numEffectedE,
|
|
const int numEffectedF,
|
|
const int curLvl)
|
|
{
|
|
const int subdivLevels = ss->subdivLevels;
|
|
const int nextLvl = curLvl + 1;
|
|
int edgeSize = ccg_edgesize(curLvl);
|
|
int ptrIdx, i;
|
|
const int vertDataSize = ss->meshIFC.vertDataSize;
|
|
float *q = ss->q, *r = ss->r;
|
|
|
|
CCGSubSurfCalcSubdivData data = {
|
|
.ss = ss,
|
|
.effectedV = effectedV,
|
|
.effectedE = effectedE,
|
|
.effectedF = effectedF,
|
|
.numEffectedV = numEffectedV,
|
|
.numEffectedE = numEffectedE,
|
|
.numEffectedF = numEffectedF,
|
|
.curLvl = curLvl,
|
|
};
|
|
|
|
{
|
|
TaskParallelSettings settings;
|
|
BLI_parallel_range_settings_defaults(&settings);
|
|
settings.min_iter_per_thread = CCG_TASK_LIMIT;
|
|
BLI_task_parallel_range(0,
|
|
numEffectedF,
|
|
&data,
|
|
ccgSubSurf__calcSubdivLevel_interior_faces_edges_midpoints_cb,
|
|
&settings);
|
|
}
|
|
|
|
/* exterior edge midpoints
|
|
* - old exterior edge points
|
|
* - new interior face midpoints
|
|
*/
|
|
/* Not worth parallelizing. */
|
|
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.0f) {
|
|
for (x = 0; x < edgeSize - 1; x++) {
|
|
int fx = x * 2 + 1;
|
|
const float *co0 = EDGE_getCo(e, curLvl, x + 0);
|
|
const float *co1 = EDGE_getCo(e, curLvl, x + 1);
|
|
float *co = EDGE_getCo(e, nextLvl, fx);
|
|
|
|
VertDataCopy(co, co0, ss);
|
|
VertDataAdd(co, co1, ss);
|
|
VertDataMulN(co, 0.5f, ss);
|
|
}
|
|
}
|
|
else {
|
|
for (x = 0; x < edgeSize - 1; x++) {
|
|
int fx = x * 2 + 1;
|
|
const float *co0 = EDGE_getCo(e, curLvl, x + 0);
|
|
const float *co1 = EDGE_getCo(e, curLvl, x + 1);
|
|
float *co = EDGE_getCo(e, nextLvl, fx);
|
|
int numFaces = 0;
|
|
|
|
VertDataCopy(q, co0, ss);
|
|
VertDataAdd(q, co1, ss);
|
|
|
|
for (j = 0; j < e->numFaces; j++) {
|
|
CCGFace *f = e->faces[j];
|
|
const int f_ed_idx = ccg_face_getEdgeIndex(f, e);
|
|
VertDataAdd(
|
|
q,
|
|
ccg_face_getIFCoEdge(f, e, f_ed_idx, nextLvl, fx, 1, subdivLevels, vertDataSize),
|
|
ss);
|
|
numFaces++;
|
|
}
|
|
|
|
VertDataMulN(q, 1.0f / (2.0f + numFaces), ss);
|
|
|
|
VertDataCopy(r, co0, ss);
|
|
VertDataAdd(r, co1, ss);
|
|
VertDataMulN(r, 0.5f, ss);
|
|
|
|
VertDataCopy(co, q, ss);
|
|
VertDataSub(r, q, ss);
|
|
VertDataMulN(r, sharpness, ss);
|
|
VertDataAdd(co, r, ss);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* exterior vertex shift
|
|
* - old vertex points (shifting)
|
|
* - old exterior edge points
|
|
* - new interior face midpoints
|
|
*/
|
|
/* Not worth parallelizing. */
|
|
for (ptrIdx = 0; ptrIdx < numEffectedV; ptrIdx++) {
|
|
CCGVert *v = (CCGVert *)effectedV[ptrIdx];
|
|
const float *co = VERT_getCo(v, curLvl);
|
|
float *nCo = VERT_getCo(v, nextLvl);
|
|
int sharpCount = 0, allSharp = 1;
|
|
float avgSharpness = 0.0;
|
|
int j, seam = VERT_seam(v), seamEdges = 0;
|
|
|
|
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++;
|
|
avgSharpness += sharpness;
|
|
}
|
|
else {
|
|
allSharp = 0;
|
|
}
|
|
}
|
|
|
|
if (sharpCount) {
|
|
avgSharpness /= sharpCount;
|
|
if (avgSharpness > 1.0f) {
|
|
avgSharpness = 1.0f;
|
|
}
|
|
}
|
|
|
|
if (seamEdges < 2 || seamEdges != v->numEdges) {
|
|
seam = 0;
|
|
}
|
|
|
|
if (!v->numEdges || ss->meshIFC.simpleSubdiv) {
|
|
VertDataCopy(nCo, co, ss);
|
|
}
|
|
else if (_vert_isBoundary(v)) {
|
|
int numBoundary = 0;
|
|
|
|
VertDataZero(r, ss);
|
|
for (j = 0; j < v->numEdges; j++) {
|
|
CCGEdge *e = v->edges[j];
|
|
if (_edge_isBoundary(e)) {
|
|
VertDataAdd(r, _edge_getCoVert(e, v, curLvl, 1, vertDataSize), ss);
|
|
numBoundary++;
|
|
}
|
|
}
|
|
|
|
VertDataCopy(nCo, co, ss);
|
|
VertDataMulN(nCo, 0.75f, ss);
|
|
VertDataMulN(r, 0.25f / numBoundary, ss);
|
|
VertDataAdd(nCo, r, ss);
|
|
}
|
|
else {
|
|
const int cornerIdx = (1 + (1 << (curLvl))) - 2;
|
|
int numEdges = 0, numFaces = 0;
|
|
|
|
VertDataZero(q, ss);
|
|
for (j = 0; j < v->numFaces; j++) {
|
|
CCGFace *f = v->faces[j];
|
|
VertDataAdd(
|
|
q, FACE_getIFCo(f, nextLvl, ccg_face_getVertIndex(f, v), cornerIdx, cornerIdx), ss);
|
|
numFaces++;
|
|
}
|
|
VertDataMulN(q, 1.0f / numFaces, ss);
|
|
VertDataZero(r, ss);
|
|
for (j = 0; j < v->numEdges; j++) {
|
|
CCGEdge *e = v->edges[j];
|
|
VertDataAdd(r, _edge_getCoVert(e, v, curLvl, 1, vertDataSize), ss);
|
|
numEdges++;
|
|
}
|
|
VertDataMulN(r, 1.0f / numEdges, ss);
|
|
|
|
VertDataCopy(nCo, co, ss);
|
|
VertDataMulN(nCo, numEdges - 2.0f, ss);
|
|
VertDataAdd(nCo, q, ss);
|
|
VertDataAdd(nCo, r, ss);
|
|
VertDataMulN(nCo, 1.0f / numEdges, ss);
|
|
}
|
|
|
|
if ((sharpCount > 1 && v->numFaces) || seam) {
|
|
VertDataZero(q, ss);
|
|
|
|
if (seam) {
|
|
avgSharpness = 1.0f;
|
|
sharpCount = seamEdges;
|
|
allSharp = 1;
|
|
}
|
|
|
|
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), ss);
|
|
}
|
|
}
|
|
else if (sharpness != 0.0f) {
|
|
VertDataAdd(q, _edge_getCoVert(e, v, curLvl, 1, vertDataSize), ss);
|
|
}
|
|
}
|
|
|
|
VertDataMulN(q, (float)1 / sharpCount, ss);
|
|
|
|
if (sharpCount != 2 || allSharp) {
|
|
/* q = q + (co - q) * avgSharpness */
|
|
VertDataCopy(r, co, ss);
|
|
VertDataSub(r, q, ss);
|
|
VertDataMulN(r, avgSharpness, ss);
|
|
VertDataAdd(q, r, ss);
|
|
}
|
|
|
|
/* r = co * 0.75 + q * 0.25 */
|
|
VertDataCopy(r, co, ss);
|
|
VertDataMulN(r, 0.75f, ss);
|
|
VertDataMulN(q, 0.25f, ss);
|
|
VertDataAdd(r, q, ss);
|
|
|
|
/* nCo = nCo + (r - nCo) * avgSharpness */
|
|
VertDataSub(r, nCo, ss);
|
|
VertDataMulN(r, avgSharpness, ss);
|
|
VertDataAdd(nCo, r, ss);
|
|
}
|
|
}
|
|
|
|
/* exterior edge interior shift
|
|
* - old exterior edge midpoints (shifting)
|
|
* - old exterior edge midpoints
|
|
* - new interior face midpoints
|
|
*/
|
|
/* Not worth parallelizing. */
|
|
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;
|
|
|
|
if (sharpness != 0.0f) {
|
|
sharpCount = 2;
|
|
avgSharpness += sharpness;
|
|
|
|
if (avgSharpness > 1.0f) {
|
|
avgSharpness = 1.0f;
|
|
}
|
|
}
|
|
else {
|
|
sharpCount = 0;
|
|
avgSharpness = 0;
|
|
}
|
|
|
|
if (_edge_isBoundary(e)) {
|
|
for (x = 1; x < edgeSize - 1; x++) {
|
|
int fx = x * 2;
|
|
const float *co = EDGE_getCo(e, curLvl, x);
|
|
float *nCo = EDGE_getCo(e, nextLvl, fx);
|
|
|
|
/* Average previous level's endpoints */
|
|
VertDataCopy(r, EDGE_getCo(e, curLvl, x - 1), ss);
|
|
VertDataAdd(r, EDGE_getCo(e, curLvl, x + 1), ss);
|
|
VertDataMulN(r, 0.5f, ss);
|
|
|
|
/* nCo = nCo * 0.75 + r * 0.25 */
|
|
VertDataCopy(nCo, co, ss);
|
|
VertDataMulN(nCo, 0.75f, ss);
|
|
VertDataMulN(r, 0.25f, ss);
|
|
VertDataAdd(nCo, r, ss);
|
|
}
|
|
}
|
|
else {
|
|
for (x = 1; x < edgeSize - 1; x++) {
|
|
int fx = x * 2;
|
|
const float *co = EDGE_getCo(e, curLvl, x);
|
|
float *nCo = EDGE_getCo(e, nextLvl, fx);
|
|
int numFaces = 0;
|
|
|
|
VertDataZero(q, ss);
|
|
VertDataZero(r, ss);
|
|
VertDataAdd(r, EDGE_getCo(e, curLvl, x - 1), ss);
|
|
VertDataAdd(r, EDGE_getCo(e, curLvl, x + 1), ss);
|
|
for (j = 0; j < e->numFaces; j++) {
|
|
CCGFace *f = e->faces[j];
|
|
int f_ed_idx = ccg_face_getEdgeIndex(f, e);
|
|
VertDataAdd(
|
|
q,
|
|
ccg_face_getIFCoEdge(f, e, f_ed_idx, nextLvl, fx - 1, 1, subdivLevels, vertDataSize),
|
|
ss);
|
|
VertDataAdd(
|
|
q,
|
|
ccg_face_getIFCoEdge(f, e, f_ed_idx, nextLvl, fx + 1, 1, subdivLevels, vertDataSize),
|
|
ss);
|
|
|
|
VertDataAdd(
|
|
r,
|
|
ccg_face_getIFCoEdge(f, e, f_ed_idx, curLvl, x, 1, subdivLevels, vertDataSize),
|
|
ss);
|
|
numFaces++;
|
|
}
|
|
VertDataMulN(q, 1.0f / (numFaces * 2.0f), ss);
|
|
VertDataMulN(r, 1.0f / (2.0f + numFaces), ss);
|
|
|
|
VertDataCopy(nCo, co, ss);
|
|
VertDataMulN(nCo, (float)numFaces, ss);
|
|
VertDataAdd(nCo, q, ss);
|
|
VertDataAdd(nCo, r, ss);
|
|
VertDataMulN(nCo, 1.0f / (2 + numFaces), ss);
|
|
|
|
if (sharpCount == 2) {
|
|
VertDataCopy(q, co, ss);
|
|
VertDataMulN(q, 6.0f, ss);
|
|
VertDataAdd(q, EDGE_getCo(e, curLvl, x - 1), ss);
|
|
VertDataAdd(q, EDGE_getCo(e, curLvl, x + 1), ss);
|
|
VertDataMulN(q, 1 / 8.0f, ss);
|
|
|
|
VertDataSub(q, nCo, ss);
|
|
VertDataMulN(q, avgSharpness, ss);
|
|
VertDataAdd(nCo, q, ss);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
{
|
|
TaskParallelSettings settings;
|
|
BLI_parallel_range_settings_defaults(&settings);
|
|
settings.min_iter_per_thread = CCG_TASK_LIMIT;
|
|
BLI_task_parallel_range(0,
|
|
numEffectedF,
|
|
&data,
|
|
ccgSubSurf__calcSubdivLevel_interior_faces_edges_centerpoints_shift_cb,
|
|
&settings);
|
|
}
|
|
|
|
/* copy down */
|
|
edgeSize = ccg_edgesize(nextLvl);
|
|
|
|
/* Not worth parallelizing. */
|
|
for (i = 0; i < numEffectedE; i++) {
|
|
CCGEdge *e = effectedE[i];
|
|
VertDataCopy(EDGE_getCo(e, nextLvl, 0), VERT_getCo(e->v0, nextLvl), ss);
|
|
VertDataCopy(EDGE_getCo(e, nextLvl, edgeSize - 1), VERT_getCo(e->v1, nextLvl), ss);
|
|
}
|
|
|
|
{
|
|
TaskParallelSettings settings;
|
|
BLI_parallel_range_settings_defaults(&settings);
|
|
settings.min_iter_per_thread = CCG_TASK_LIMIT;
|
|
BLI_task_parallel_range(
|
|
0, numEffectedF, &data, ccgSubSurf__calcSubdivLevel_verts_copydata_cb, &settings);
|
|
}
|
|
}
|
|
|
|
void ccgSubSurf__sync_legacy(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;
|
|
}
|
|
}
|
|
|
|
for (j = 0; j < v->numFaces; j++) {
|
|
CCGFace *f = v->faces[j];
|
|
if (!(f->flags & Face_eEffected)) {
|
|
effectedF[numEffectedF++] = f;
|
|
f->flags |= Face_eEffected;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
curLvl = 0;
|
|
nextLvl = curLvl + 1;
|
|
|
|
for (ptrIdx = 0; ptrIdx < numEffectedF; ptrIdx++) {
|
|
CCGFace *f = effectedF[ptrIdx];
|
|
void *co = FACE_getCenterData(f);
|
|
VertDataZero(co, ss);
|
|
for (i = 0; i < f->numVerts; i++) {
|
|
VertDataAdd(co, VERT_getCo(FACE_getVerts(f)[i], curLvl), ss);
|
|
}
|
|
VertDataMulN(co, 1.0f / f->numVerts, ss);
|
|
|
|
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.0f) {
|
|
VertDataCopy(co, VERT_getCo(e->v0, curLvl), ss);
|
|
VertDataAdd(co, VERT_getCo(e->v1, curLvl), ss);
|
|
VertDataMulN(co, 0.5f, ss);
|
|
}
|
|
else {
|
|
int numFaces = 0;
|
|
VertDataCopy(q, VERT_getCo(e->v0, curLvl), ss);
|
|
VertDataAdd(q, VERT_getCo(e->v1, curLvl), ss);
|
|
for (i = 0; i < e->numFaces; i++) {
|
|
CCGFace *f = e->faces[i];
|
|
VertDataAdd(q, (float *)FACE_getCenterData(f), ss);
|
|
numFaces++;
|
|
}
|
|
VertDataMulN(q, 1.0f / (2.0f + numFaces), ss);
|
|
|
|
VertDataCopy(r, VERT_getCo(e->v0, curLvl), ss);
|
|
VertDataAdd(r, VERT_getCo(e->v1, curLvl), ss);
|
|
VertDataMulN(r, 0.5f, ss);
|
|
|
|
VertDataCopy(co, q, ss);
|
|
VertDataSub(r, q, ss);
|
|
VertDataMulN(r, sharpness, ss);
|
|
VertDataAdd(co, r, ss);
|
|
}
|
|
|
|
/* 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);
|
|
|
|
if (seam && _edge_isBoundary(e)) {
|
|
seamEdges++;
|
|
}
|
|
|
|
if (sharpness != 0.0f) {
|
|
sharpCount++;
|
|
avgSharpness += sharpness;
|
|
}
|
|
else {
|
|
allSharp = 0;
|
|
}
|
|
}
|
|
|
|
if (sharpCount) {
|
|
avgSharpness /= sharpCount;
|
|
if (avgSharpness > 1.0f) {
|
|
avgSharpness = 1.0f;
|
|
}
|
|
}
|
|
|
|
if (seamEdges < 2 || seamEdges != v->numEdges) {
|
|
seam = 0;
|
|
}
|
|
|
|
if (!v->numEdges || ss->meshIFC.simpleSubdiv) {
|
|
VertDataCopy(nCo, co, ss);
|
|
}
|
|
else if (_vert_isBoundary(v)) {
|
|
int numBoundary = 0;
|
|
|
|
VertDataZero(r, ss);
|
|
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), ss);
|
|
numBoundary++;
|
|
}
|
|
}
|
|
VertDataCopy(nCo, co, ss);
|
|
VertDataMulN(nCo, 0.75f, ss);
|
|
VertDataMulN(r, 0.25f / numBoundary, ss);
|
|
VertDataAdd(nCo, r, ss);
|
|
}
|
|
else {
|
|
int numEdges = 0, numFaces = 0;
|
|
|
|
VertDataZero(q, ss);
|
|
for (i = 0; i < v->numFaces; i++) {
|
|
CCGFace *f = v->faces[i];
|
|
VertDataAdd(q, (float *)FACE_getCenterData(f), ss);
|
|
numFaces++;
|
|
}
|
|
VertDataMulN(q, 1.0f / numFaces, ss);
|
|
VertDataZero(r, ss);
|
|
for (i = 0; i < v->numEdges; i++) {
|
|
CCGEdge *e = v->edges[i];
|
|
VertDataAdd(r, VERT_getCo(_edge_getOtherVert(e, v), curLvl), ss);
|
|
numEdges++;
|
|
}
|
|
VertDataMulN(r, 1.0f / numEdges, ss);
|
|
|
|
VertDataCopy(nCo, co, ss);
|
|
VertDataMulN(nCo, numEdges - 2.0f, ss);
|
|
VertDataAdd(nCo, q, ss);
|
|
VertDataAdd(nCo, r, ss);
|
|
VertDataMulN(nCo, 1.0f / numEdges, ss);
|
|
}
|
|
|
|
if (sharpCount > 1 || seam) {
|
|
VertDataZero(q, ss);
|
|
|
|
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)) {
|
|
CCGVert *oV = _edge_getOtherVert(e, v);
|
|
VertDataAdd(q, VERT_getCo(oV, curLvl), ss);
|
|
}
|
|
}
|
|
else if (sharpness != 0.0f) {
|
|
CCGVert *oV = _edge_getOtherVert(e, v);
|
|
VertDataAdd(q, VERT_getCo(oV, curLvl), ss);
|
|
}
|
|
}
|
|
|
|
VertDataMulN(q, (float)1 / sharpCount, ss);
|
|
|
|
if (sharpCount != 2 || allSharp) {
|
|
/* q = q + (co - q) * avgSharpness */
|
|
VertDataCopy(r, co, ss);
|
|
VertDataSub(r, q, ss);
|
|
VertDataMulN(r, avgSharpness, ss);
|
|
VertDataAdd(q, r, ss);
|
|
}
|
|
|
|
/* r = co * 0.75 + q * 0.25 */
|
|
VertDataCopy(r, co, ss);
|
|
VertDataMulN(r, 0.75f, ss);
|
|
VertDataMulN(q, 0.25f, ss);
|
|
VertDataAdd(r, q, ss);
|
|
|
|
/* nCo = nCo + (r - nCo) * avgSharpness */
|
|
VertDataSub(r, nCo, ss);
|
|
VertDataMulN(r, avgSharpness, ss);
|
|
VertDataAdd(nCo, r, ss);
|
|
}
|
|
|
|
/* 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), ss);
|
|
VertDataCopy(EDGE_getCo(e, nextLvl, 2), VERT_getCo(e->v1, nextLvl), ss);
|
|
}
|
|
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), (float *)FACE_getCenterData(f), ss);
|
|
VertDataCopy(FACE_getIECo(f, nextLvl, S, 0), (float *)FACE_getCenterData(f), ss);
|
|
VertDataCopy(
|
|
FACE_getIFCo(f, nextLvl, S, 1, 1), VERT_getCo(FACE_getVerts(f)[S], nextLvl), ss);
|
|
VertDataCopy(
|
|
FACE_getIECo(f, nextLvl, S, 1), EDGE_getCo(FACE_getEdges(f)[S], nextLvl, 1), ss);
|
|
|
|
VertDataCopy(FACE_getIFCo(f, nextLvl, S, 1, 0),
|
|
_edge_getCoVert(e, FACE_getVerts(f)[S], nextLvl, 1, vertDataSize),
|
|
ss);
|
|
VertDataCopy(FACE_getIFCo(f, nextLvl, S, 0, 1),
|
|
_edge_getCoVert(prevE, FACE_getVerts(f)[S], nextLvl, 1, vertDataSize),
|
|
ss);
|
|
}
|
|
}
|
|
|
|
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);
|
|
|
|
#ifdef DUMP_RESULT_GRIDS
|
|
ccgSubSurf__dumpCoords(ss);
|
|
#endif
|
|
}
|
|
|
|
/* ** Public API exposed to other areas which depends on old CCG code. ** */
|
|
|
|
CCGError ccgSubSurf_updateNormals(CCGSubSurf *ss, CCGFace **effectedF, int numEffectedF)
|
|
{
|
|
CCGVert **effectedV;
|
|
CCGEdge **effectedE;
|
|
int i, numEffectedV, numEffectedE, freeF;
|
|
|
|
ccgSubSurf__allFaces(ss, &effectedF, &numEffectedF, &freeF);
|
|
ccgSubSurf__effectedFaceNeighbors(
|
|
ss, effectedF, numEffectedF, &effectedV, &numEffectedV, &effectedE, &numEffectedE);
|
|
|
|
if (ss->calcVertNormals) {
|
|
ccgSubSurf__calcVertNormals(
|
|
ss, effectedV, effectedE, effectedF, numEffectedV, numEffectedE, numEffectedF);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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__effectedFaceNeighbors(
|
|
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;
|
|
}
|