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

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/blenkernel/intern/CCGSubSurf_opensubdiv.c')
-rw-r--r--source/blender/blenkernel/intern/CCGSubSurf_opensubdiv.c908
1 files changed, 908 insertions, 0 deletions
diff --git a/source/blender/blenkernel/intern/CCGSubSurf_opensubdiv.c b/source/blender/blenkernel/intern/CCGSubSurf_opensubdiv.c
new file mode 100644
index 00000000000..c6c8f14dd2c
--- /dev/null
+++ b/source/blender/blenkernel/intern/CCGSubSurf_opensubdiv.c
@@ -0,0 +1,908 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/blenkernel/intern/CCGSubSurf_opensubdiv.c
+ * \ingroup bke
+ */
+
+#ifdef WITH_OPENSUBDIV
+
+#include "MEM_guardedalloc.h"
+#include "BLI_sys_types.h" // for intptr_t support
+
+#include "BLI_utildefines.h" /* for BLI_assert */
+#include "BLI_math.h"
+
+#include "CCGSubSurf.h"
+#include "CCGSubSurf_intern.h"
+
+#include "BKE_DerivedMesh.h"
+
+#include "DNA_userdef_types.h"
+
+#include "opensubdiv_capi.h"
+#include "opensubdiv_converter_capi.h"
+
+#include "GL/glew.h"
+
+#define OSD_LOG if (false) printf
+
+static bool compare_ccg_derivedmesh_topology(CCGSubSurf *ss, DerivedMesh *dm)
+{
+ const int num_verts = dm->getNumVerts(dm);
+ const int num_edges = dm->getNumEdges(dm);
+ const int num_polys = dm->getNumPolys(dm);
+ const MEdge *medge = dm->getEdgeArray(dm);
+ const MLoop *mloop = dm->getLoopArray(dm);
+ const MPoly *mpoly = dm->getPolyArray(dm);
+
+ /* Quick preliminary tests based on the number of verts and facces. */
+ {
+ if (num_verts != ss->vMap->numEntries ||
+ num_edges != ss->eMap->numEntries ||
+ num_polys != ss->fMap->numEntries)
+ {
+ return false;
+ }
+ }
+
+ /* Rather slow check for faces topology change. */
+ {
+ CCGFaceIterator ccg_face_iter;
+ for (ccgSubSurf_initFaceIterator(ss, &ccg_face_iter);
+ !ccgFaceIterator_isStopped(&ccg_face_iter);
+ ccgFaceIterator_next(&ccg_face_iter))
+ {
+ /*const*/ CCGFace *ccg_face = ccgFaceIterator_getCurrent(&ccg_face_iter);
+ const int poly_index = GET_INT_FROM_POINTER(ccgSubSurf_getFaceFaceHandle(ccg_face));
+ const MPoly *mp = &mpoly[poly_index];
+ int corner;
+ if (ccg_face->numVerts != mp->totloop) {
+ return false;
+ }
+ for (corner = 0; corner < ccg_face->numVerts; corner++) {
+ /*const*/ CCGVert *ccg_vert = FACE_getVerts(ccg_face)[corner];
+ const int vert_index = GET_INT_FROM_POINTER(ccgSubSurf_getVertVertHandle(ccg_vert));
+ if (vert_index != mloop[mp->loopstart + corner].v) {
+ return false;
+ }
+ }
+ }
+ }
+
+ /* Check for edge topology change. */
+ {
+ CCGEdgeIterator ccg_edge_iter;
+ for (ccgSubSurf_initEdgeIterator(ss, &ccg_edge_iter);
+ !ccgEdgeIterator_isStopped(&ccg_edge_iter);
+ ccgEdgeIterator_next(&ccg_edge_iter))
+ {
+ /* const */ CCGEdge *ccg_edge = ccgEdgeIterator_getCurrent(&ccg_edge_iter);
+ /* const */ CCGVert *ccg_vert1 = ccg_edge->v0;
+ /* const */ CCGVert *ccg_vert2 = ccg_edge->v1;
+ const int ccg_vert1_index = GET_INT_FROM_POINTER(ccgSubSurf_getVertVertHandle(ccg_vert1));
+ const int ccg_vert2_index = GET_INT_FROM_POINTER(ccgSubSurf_getVertVertHandle(ccg_vert2));
+ const int edge_index = GET_INT_FROM_POINTER(ccgSubSurf_getEdgeEdgeHandle(ccg_edge));
+ const MEdge *me = &medge[edge_index];
+ if (me->v1 != ccg_vert1_index || me->v2 != ccg_vert2_index) {
+ return false;
+ }
+ }
+ }
+
+ /* TODO(sergey): Crease topology changes detection. */
+ {
+ CCGEdgeIterator ccg_edge_iter;
+ for (ccgSubSurf_initEdgeIterator(ss, &ccg_edge_iter);
+ !ccgEdgeIterator_isStopped(&ccg_edge_iter);
+ ccgEdgeIterator_next(&ccg_edge_iter))
+ {
+ /* const */ CCGEdge *ccg_edge = ccgEdgeIterator_getCurrent(&ccg_edge_iter);
+ const int edge_index = GET_INT_FROM_POINTER(ccgSubSurf_getEdgeEdgeHandle(ccg_edge));
+ if (ccg_edge->crease != medge[edge_index].crease) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+static bool compare_osd_derivedmesh_topology(CCGSubSurf *ss, DerivedMesh *dm)
+{
+ const OpenSubdiv_TopologyRefinerDescr *topology_refiner;
+ OpenSubdiv_Converter converter;
+ bool result;
+ if (ss->osd_mesh == NULL && ss->osd_topology_refiner == NULL) {
+ return true;
+ }
+ /* TODO(sergey): De-duplicate with topology counter at the bottom of
+ * the file.
+ */
+ if (ss->osd_topology_refiner != NULL) {
+ topology_refiner = ss->osd_topology_refiner;
+ }
+ else {
+ topology_refiner = openSubdiv_getGLMeshTopologyRefiner(ss->osd_mesh);
+ }
+ ccgSubSurf_converter_setup_from_derivedmesh(ss, dm, &converter);
+ result = openSubdiv_topologyRefnerCompareConverter(topology_refiner,
+ &converter);
+ ccgSubSurf_converter_free(&converter);
+ return result;
+}
+
+static bool opensubdiv_is_topology_changed(CCGSubSurf *ss, DerivedMesh *dm)
+{
+ if (ss->osd_compute != U.opensubdiv_compute_type) {
+ return true;
+ }
+ if (ss->osd_topology_refiner != NULL) {
+ int levels = openSubdiv_topologyRefinerGetSubdivLevel(
+ ss->osd_topology_refiner);
+ BLI_assert(ss->osd_mesh_invalid == true);
+ if (levels != ss->subdivLevels) {
+ return true;
+ }
+ }
+ if (ss->osd_mesh != NULL && ss->osd_mesh_invalid == false) {
+ const OpenSubdiv_TopologyRefinerDescr *topology_refiner =
+ openSubdiv_getGLMeshTopologyRefiner(ss->osd_mesh);
+ int levels = openSubdiv_topologyRefinerGetSubdivLevel(topology_refiner);
+ BLI_assert(ss->osd_topology_refiner == NULL);
+ if (levels != ss->subdivLevels) {
+ return true;
+ }
+ }
+ if (ss->skip_grids == false) {
+ return compare_ccg_derivedmesh_topology(ss, dm) == false;
+ }
+ else {
+ return compare_osd_derivedmesh_topology(ss, dm) == false;
+ }
+ return false;
+}
+
+void ccgSubSurf_checkTopologyChanged(CCGSubSurf *ss, DerivedMesh *dm)
+{
+ if (opensubdiv_is_topology_changed(ss, dm)) {
+ /* ** Make sure both GPU and CPU backends are properly reset. ** */
+
+ ss->osd_coarse_coords_invalid = true;
+ ss->osd_uvs_invalid = true;
+
+ /* Reset GPU part. */
+ ss->osd_mesh_invalid = true;
+ if (ss->osd_topology_refiner != NULL) {
+ openSubdiv_deleteTopologyRefinerDescr(ss->osd_topology_refiner);
+ ss->osd_topology_refiner = NULL;
+ }
+
+ /* Reste CPU side. */
+ if (ss->osd_evaluator != NULL) {
+ openSubdiv_deleteEvaluatorDescr(ss->osd_evaluator);
+ ss->osd_evaluator = NULL;
+ }
+ }
+}
+
+static void ccgSubSurf__updateGLMeshCoords(CCGSubSurf *ss)
+{
+ BLI_assert(ss->meshIFC.numLayers == 3);
+ openSubdiv_osdGLMeshUpdateVertexBuffer(ss->osd_mesh,
+ (float *) ss->osd_coarse_coords,
+ 0,
+ ss->osd_num_coarse_coords);
+}
+
+bool ccgSubSurf_prepareGLMesh(CCGSubSurf *ss, bool use_osd_glsl)
+{
+ int compute_type;
+
+ switch (U.opensubdiv_compute_type) {
+#define CHECK_COMPUTE_TYPE(type) \
+ case USER_OPENSUBDIV_COMPUTE_ ## type: \
+ compute_type = OPENSUBDIV_EVALUATOR_ ## type; \
+ break;
+ CHECK_COMPUTE_TYPE(CPU)
+ CHECK_COMPUTE_TYPE(OPENMP)
+ CHECK_COMPUTE_TYPE(OPENCL)
+ CHECK_COMPUTE_TYPE(CUDA)
+ CHECK_COMPUTE_TYPE(GLSL_TRANSFORM_FEEDBACK)
+ CHECK_COMPUTE_TYPE(GLSL_COMPUTE)
+#undef CHECK_COMPUTE_TYPE
+ }
+
+ if (ss->osd_vao == 0) {
+ glGenVertexArrays(1, &ss->osd_vao);
+ }
+
+ if (ss->osd_mesh_invalid) {
+ if (ss->osd_mesh != NULL) {
+ openSubdiv_deleteOsdGLMesh(ss->osd_mesh);
+ ss->osd_mesh = NULL;
+ }
+ ss->osd_mesh_invalid = false;
+ }
+
+ if (ss->osd_mesh == NULL) {
+ if (ss->osd_topology_refiner == NULL) {
+ /* Happens with empty meshes. */
+ /* TODO(sergey): Add assert that mesh is indeed empty. */
+ return false;
+ }
+
+ ss->osd_mesh = openSubdiv_createOsdGLMeshFromTopologyRefiner(
+ ss->osd_topology_refiner,
+ compute_type,
+ ss->subdivLevels,
+ ss->osd_subsurf_uv);
+ ss->osd_topology_refiner = NULL;
+
+ if (UNLIKELY(ss->osd_mesh == NULL)) {
+ /* Most likely compute device is not available. */
+ return false;
+ }
+
+ ccgSubSurf__updateGLMeshCoords(ss);
+
+ openSubdiv_osdGLMeshRefine(ss->osd_mesh);
+ openSubdiv_osdGLMeshSynchronize(ss->osd_mesh);
+
+ glBindVertexArray(ss->osd_vao);
+ glBindBuffer(GL_ARRAY_BUFFER,
+ openSubdiv_getOsdGLMeshVertexBuffer(ss->osd_mesh));
+
+ glEnableVertexAttribArray(0);
+ glEnableVertexAttribArray(1);
+ glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE,
+ sizeof(GLfloat) * 6, 0);
+ glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE,
+ sizeof(GLfloat) * 6, (float *)12);
+
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ }
+ else if (ss->osd_coarse_coords_invalid) {
+ ccgSubSurf__updateGLMeshCoords(ss);
+ openSubdiv_osdGLMeshRefine(ss->osd_mesh);
+ openSubdiv_osdGLMeshSynchronize(ss->osd_mesh);
+ ss->osd_coarse_coords_invalid = false;
+ }
+
+ openSubdiv_osdGLMeshDisplayPrepare(use_osd_glsl, ss->osd_uv_index);
+
+ return true;
+}
+
+void ccgSubSurf_drawGLMesh(CCGSubSurf *ss, bool fill_quads,
+ int start_partition, int num_partitions)
+{
+ if (LIKELY(ss->osd_mesh != NULL)) {
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,
+ openSubdiv_getOsdGLMeshPatchIndexBuffer(ss->osd_mesh));
+
+ openSubdiv_osdGLMeshBindVertexBuffer(ss->osd_mesh);
+ glBindVertexArray(ss->osd_vao);
+ openSubdiv_osdGLMeshDisplay(ss->osd_mesh, fill_quads,
+ start_partition, num_partitions);
+ glBindVertexArray(0);
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+ }
+}
+
+void ccgSubSurf_setSkipGrids(CCGSubSurf *ss, bool skip_grids)
+{
+ ss->skip_grids = skip_grids;
+}
+
+bool ccgSubSurf_needGrids(CCGSubSurf *ss)
+{
+ return ss->skip_grids == false;
+}
+
+BLI_INLINE void ccgSubSurf__mapGridToFace(int S, float grid_u, float grid_v,
+ float *face_u, float *face_v)
+{
+ float u, v;
+
+ /* - Each grid covers half of the face along the edges.
+ * - Grid's (0, 0) starts from the middle of the face.
+ */
+ u = 0.5f - 0.5f * grid_u;
+ v = 0.5f - 0.5f * grid_v;
+
+ if (S == 0) {
+ *face_u = v;
+ *face_v = u;
+ }
+ else if (S == 1) {
+ *face_u = 1.0f - u;
+ *face_v = v;
+ }
+ else if (S == 2) {
+ *face_u = 1.0f - v;
+ *face_v = 1.0f - u;
+ }
+ else {
+ *face_u = u;
+ *face_v = 1.0f - v;
+ }
+}
+
+BLI_INLINE void ccgSubSurf__mapEdgeToFace(int S,
+ int edge_segment,
+ bool inverse_edge,
+ int edgeSize,
+ float *face_u, float *face_v)
+{
+ int t = inverse_edge ? edgeSize - edge_segment - 1 : edge_segment;
+ if (S == 0) {
+ *face_u = (float) t / (edgeSize - 1);
+ *face_v = 0.0f;
+ }
+ else if (S == 1) {
+ *face_u = 1.0f;
+ *face_v = (float) t / (edgeSize - 1);
+ }
+ else if (S == 2) {
+ *face_u = 1.0f - (float) t / (edgeSize - 1);
+ *face_v = 1.0f;
+ }
+ else {
+ *face_u = 0.0f;
+ *face_v = 1.0f - (float) t / (edgeSize - 1);
+ }
+}
+
+void ccgSubSurf_evaluatorSetFVarUV(CCGSubSurf *ss,
+ DerivedMesh *dm,
+ int layer_index)
+{
+ MPoly *mpoly = dm->getPolyArray(dm);
+ MLoopUV *mloopuv = CustomData_get_layer_n(&dm->loopData, CD_MLOOPUV, layer_index);
+ int num_polys = dm->getNumPolys(dm);
+ int index, poly;
+ BLI_assert(ss->osd_evaluator != NULL);
+ for (poly = 0, index = 0; poly < num_polys; poly++) {
+ int loop;
+ MPoly *mp = &mpoly[poly];
+ for (loop = 0; loop < mp->totloop; loop++, index++) {
+ MLoopUV *mluv = &mloopuv[loop + mp->loopstart];
+ (void)mluv;
+ /* TODO(sergey): Send mluv->uv to the evaluator's face varying
+ * buffer.
+ */
+ }
+ }
+ (void)ss;
+}
+
+void ccgSubSurf_evaluatorFVarUV(CCGSubSurf *ss,
+ int face_index, int S,
+ float grid_u, float grid_v,
+ float uv[2])
+{
+ float face_u, face_v;
+ ccgSubSurf__mapGridToFace(S,
+ grid_u, grid_v,
+ &face_u, &face_v);
+ (void)ss;
+ (void)face_index;
+ /* TODO(sergey): Evaluate face varying coordinate. */
+ zero_v2(uv);
+}
+
+static bool opensubdiv_createEvaluator(CCGSubSurf *ss)
+{
+ OpenSubdiv_Converter converter;
+ OpenSubdiv_TopologyRefinerDescr *topology_refiner;
+ if (ss->fMap->numEntries == 0) {
+ /* OpenSubdiv doesn't support meshes without faces. */
+ return false;
+ }
+ ccgSubSurf_converter_setup_from_ccg(ss, &converter);
+ topology_refiner = openSubdiv_createTopologyRefinerDescr(&converter);
+ ccgSubSurf_converter_free(&converter);
+ ss->osd_evaluator =
+ openSubdiv_createEvaluatorDescr(topology_refiner,
+ ss->subdivLevels);
+ if (ss->osd_evaluator == NULL) {
+ BLI_assert(!"OpenSubdiv initialization failed, should not happen.");
+ return false;
+ }
+ return true;
+}
+
+static bool opensubdiv_ensureEvaluator(CCGSubSurf *ss)
+{
+ if (ss->osd_evaluator == NULL) {
+ OSD_LOG("Allocating new evaluator, %d verts\n", ss->vMap->numEntries);
+ opensubdiv_createEvaluator(ss);
+ }
+ return ss->osd_evaluator != NULL;
+}
+
+static void opensubdiv_updateEvaluatorCoarsePositions(CCGSubSurf *ss)
+{
+ float (*positions)[3];
+ int vertDataSize = ss->meshIFC.vertDataSize;
+ int num_basis_verts = ss->vMap->numEntries;
+ int i;
+
+ /* TODO(sergey): Avoid allocation on every update. We could either update
+ * coordinates in chunks of 1K vertices (which will only use stack memory)
+ * or do some callback magic for OSD evaluator can invoke it and fill in
+ * buffer directly.
+ */
+ if (ss->meshIFC.numLayers == 3) {
+ /* If all the components are to be initialized, no need to memset the
+ * new memory block.
+ */
+ positions = MEM_mallocN(3 * sizeof(float) * num_basis_verts,
+ "OpenSubdiv coarse points");
+ }
+ else {
+ /* Calloc in order to have z component initialized to 0 for Uvs */
+ positions = MEM_callocN(3 * sizeof(float) * num_basis_verts,
+ "OpenSubdiv coarse points");
+ }
+#pragma omp parallel for
+ for (i = 0; i < ss->vMap->curSize; i++) {
+ CCGVert *v = (CCGVert *) ss->vMap->buckets[i];
+ for (; v; v = v->next) {
+ float *co = VERT_getCo(v, 0);
+ BLI_assert(v->osd_index < ss->vMap->numEntries);
+ VertDataCopy(positions[v->osd_index], co, ss);
+ OSD_LOG("Point %d has value %f %f %f\n",
+ v->osd_index,
+ positions[v->osd_index][0],
+ positions[v->osd_index][1],
+ positions[v->osd_index][2]);
+ }
+ }
+
+ openSubdiv_setEvaluatorCoarsePositions(ss->osd_evaluator,
+ (float *)positions,
+ 0,
+ num_basis_verts);
+
+ MEM_freeN(positions);
+}
+
+static void opensubdiv_evaluateQuadFaceGrids(CCGSubSurf *ss,
+ CCGFace *face,
+ const int osd_face_index)
+{
+ int normalDataOffset = ss->normalDataOffset;
+ int subdivLevels = ss->subdivLevels;
+ int gridSize = ccg_gridsize(subdivLevels);
+ int edgeSize = ccg_edgesize(subdivLevels);
+ int vertDataSize = ss->meshIFC.vertDataSize;
+ int S;
+ bool do_normals = ss->meshIFC.numLayers == 3;
+
+#pragma omp parallel for
+ for (S = 0; S < face->numVerts; S++) {
+ int x, y, k;
+ CCGEdge *edge = NULL;
+ bool inverse_edge;
+
+ for (x = 0; x < gridSize; x++) {
+ for (y = 0; y < gridSize; y++) {
+ float *co = FACE_getIFCo(face, subdivLevels, S, x, y);
+ float *no = FACE_getIFNo(face, subdivLevels, S, x, y);
+ float grid_u = (float) x / (gridSize - 1),
+ grid_v = (float) y / (gridSize - 1);
+ float face_u, face_v;
+ float P[3], dPdu[3], dPdv[3];
+
+ ccgSubSurf__mapGridToFace(S, grid_u, grid_v, &face_u, &face_v);
+
+ /* TODO(sergey): Need proper port. */
+ openSubdiv_evaluateLimit(ss->osd_evaluator, osd_face_index,
+ face_u, face_v,
+ P,
+ do_normals ? dPdu : NULL,
+ do_normals ? dPdv : NULL);
+
+ OSD_LOG("face=%d, corner=%d, grid_u=%f, grid_v=%f, face_u=%f, face_v=%f, P=(%f, %f, %f)\n",
+ osd_face_index, S, grid_u, grid_v, face_u, face_v, P[0], P[1], P[2]);
+
+ VertDataCopy(co, P, ss);
+ if (do_normals) {
+ cross_v3_v3v3(no, dPdu, dPdv);
+ normalize_v3(no);
+ }
+
+ if (x == gridSize - 1 && y == gridSize - 1) {
+ float *vert_co = VERT_getCo(FACE_getVerts(face)[S], subdivLevels);
+ VertDataCopy(vert_co, co, ss);
+ if (do_normals) {
+ float *vert_no = VERT_getNo(FACE_getVerts(face)[S], subdivLevels);
+ VertDataCopy(vert_no, no, ss);
+ }
+ }
+ if (S == 0 && x == 0 && y == 0) {
+ float *center_co = (float *)FACE_getCenterData(face);
+ VertDataCopy(center_co, co, ss);
+ if (do_normals) {
+ float *center_no = (float *)((byte *)FACE_getCenterData(face) + normalDataOffset);
+ VertDataCopy(center_no, no, ss);
+ }
+ }
+ }
+ }
+
+ for (x = 0; x < gridSize; x++) {
+ VertDataCopy(FACE_getIECo(face, subdivLevels, S, x),
+ FACE_getIFCo(face, subdivLevels, S, x, 0), ss);
+ if (do_normals) {
+ VertDataCopy(FACE_getIENo(face, subdivLevels, S, x),
+ FACE_getIFNo(face, subdivLevels, S, x, 0), ss);
+ }
+ }
+
+ for (k = 0; k < face->numVerts; k++) {
+ CCGEdge *current_edge = FACE_getEdges(face)[k];
+ CCGVert **face_verts = FACE_getVerts(face);
+ if (current_edge->v0 == face_verts[S] &&
+ current_edge->v1 == face_verts[(S + 1) % face->numVerts])
+ {
+ edge = current_edge;
+ inverse_edge = false;
+ break;
+ }
+ if (current_edge->v1 == face_verts[S] &&
+ current_edge->v0 == face_verts[(S + 1) % face->numVerts])
+ {
+ edge = current_edge;
+ inverse_edge = true;
+ break;
+ }
+ }
+
+ BLI_assert(edge != NULL);
+
+ for (x = 0; x < edgeSize; x++) {
+ float u = 0, v = 0;
+ float *co = EDGE_getCo(edge, subdivLevels, x);
+ float *no = EDGE_getNo(edge, subdivLevels, x);
+ float P[3], dPdu[3], dPdv[3];
+ ccgSubSurf__mapEdgeToFace(S, x,
+ inverse_edge,
+ edgeSize,
+ &u, &v);
+
+ /* TODO(sergey): Ideally we will re-use grid here, but for now
+ * let's just re-evaluate for simplicity.
+ */
+ /* TODO(sergey): Need proper port. */
+ openSubdiv_evaluateLimit(ss->osd_evaluator, osd_face_index, u, v, P, dPdu, dPdv);
+ VertDataCopy(co, P, ss);
+ if (do_normals) {
+ cross_v3_v3v3(no, dPdu, dPdv);
+ normalize_v3(no);
+ }
+ }
+ }
+}
+
+static void opensubdiv_evaluateNGonFaceGrids(CCGSubSurf *ss,
+ CCGFace *face,
+ const int osd_face_index)
+{
+ CCGVert **all_verts = FACE_getVerts(face);
+ int normalDataOffset = ss->normalDataOffset;
+ int subdivLevels = ss->subdivLevels;
+ int gridSize = ccg_gridsize(subdivLevels);
+ int edgeSize = ccg_edgesize(subdivLevels);
+ int vertDataSize = ss->meshIFC.vertDataSize;
+ int S;
+ bool do_normals = ss->meshIFC.numLayers == 3;
+
+ /* Note about handling non-quad faces.
+ *
+ * In order to deal with non-quad faces we need to split them
+ * into a quads in the following way:
+ *
+ * |
+ * (vert_next)
+ * |
+ * |
+ * |
+ * (face_center) ------------------- (v2)
+ * | (o)--------------------> |
+ * | | v |
+ * | | |
+ * | | |
+ * | | |
+ * | | y ^ |
+ * | | | |
+ * | v u x | |
+ * | <---(o) |
+ * ---- (vert_prev) ---- (v1) -------------------- (vert)
+ *
+ * This is how grids are expected to be stored and it's how
+ * OpenSubdiv deals with non-quad faces using ptex face indices.
+ * We only need to convert ptex (x, y) to grid (u, v) by some
+ * simple flips and evaluate the ptex face.
+ */
+
+ /* Evaluate face grids. */
+#pragma omp parallel for
+ for (S = 0; S < face->numVerts; S++) {
+ int x, y;
+ for (x = 0; x < gridSize; x++) {
+ for (y = 0; y < gridSize; y++) {
+ float *co = FACE_getIFCo(face, subdivLevels, S, x, y);
+ float *no = FACE_getIFNo(face, subdivLevels, S, x, y);
+ float u = 1.0f - (float) y / (gridSize - 1),
+ v = 1.0f - (float) x / (gridSize - 1);
+ float P[3], dPdu[3], dPdv[3];
+
+ /* TODO(sergey): Need proper port. */
+ openSubdiv_evaluateLimit(ss->osd_evaluator, osd_face_index + S, u, v, P, dPdu, dPdv);
+
+ OSD_LOG("face=%d, corner=%d, u=%f, v=%f, P=(%f, %f, %f)\n",
+ osd_face_index + S, S, u, v, P[0], P[1], P[2]);
+
+ VertDataCopy(co, P, ss);
+ if (do_normals) {
+ cross_v3_v3v3(no, dPdu, dPdv);
+ normalize_v3(no);
+ }
+
+ /* TODO(sergey): De-dpuplicate with the quad case. */
+ if (x == gridSize - 1 && y == gridSize - 1) {
+ float *vert_co = VERT_getCo(FACE_getVerts(face)[S], subdivLevels);
+ VertDataCopy(vert_co, co, ss);
+ if (do_normals) {
+ float *vert_no = VERT_getNo(FACE_getVerts(face)[S], subdivLevels);
+ VertDataCopy(vert_no, no, ss);
+ }
+ }
+ if (S == 0 && x == 0 && y == 0) {
+ float *center_co = (float *)FACE_getCenterData(face);
+ VertDataCopy(center_co, co, ss);
+ if (do_normals) {
+ float *center_no = (float *)((byte *)FACE_getCenterData(face) + normalDataOffset);
+ VertDataCopy(center_no, no, ss);
+ }
+ }
+ }
+ }
+ for (x = 0; x < gridSize; x++) {
+ VertDataCopy(FACE_getIECo(face, subdivLevels, S, x),
+ FACE_getIFCo(face, subdivLevels, S, x, 0), ss);
+ if (do_normals) {
+ VertDataCopy(FACE_getIENo(face, subdivLevels, S, x),
+ FACE_getIFNo(face, subdivLevels, S, x, 0), ss);
+ }
+ }
+ }
+
+ /* Evaluate edges. */
+ for (S = 0; S < face->numVerts; S++) {
+ CCGEdge *edge = FACE_getEdges(face)[S];
+ int x, S0, S1;
+ bool flip;
+
+ for (x = 0; x < face->numVerts; ++x) {
+ if (all_verts[x] == edge->v0) {
+ S0 = x;
+ }
+ else if (all_verts[x] == edge->v1) {
+ S1 = x;
+ }
+ }
+ if (S == face->numVerts - 1) {
+ flip = S0 > S1;
+ }
+ else {
+ flip = S0 < S1;
+ }
+
+ for (x = 0; x <= edgeSize / 2; x++) {
+ float *edge_co = EDGE_getCo(edge, subdivLevels, x);
+ float *edge_no = EDGE_getNo(edge, subdivLevels, x);
+ float *face_edge_co;
+ float *face_edge_no;
+ if (flip) {
+ face_edge_co = FACE_getIFCo(face, subdivLevels, S0, gridSize - 1, gridSize - 1 - x);
+ face_edge_no = FACE_getIFNo(face, subdivLevels, S0, gridSize - 1, gridSize - 1 - x);
+ }
+ else {
+ face_edge_co = FACE_getIFCo(face, subdivLevels, S0, gridSize - 1 - x, gridSize - 1);
+ face_edge_no = FACE_getIFNo(face, subdivLevels, S0, gridSize - 1 - x, gridSize - 1);
+ }
+ VertDataCopy(edge_co, face_edge_co, ss);
+ if (do_normals) {
+ VertDataCopy(edge_no, face_edge_no, ss);
+ }
+ }
+ for (x = edgeSize / 2 + 1; x < edgeSize; x++) {
+ float *edge_co = EDGE_getCo(edge, subdivLevels, x);
+ float *edge_no = EDGE_getNo(edge, subdivLevels, x);
+ float *face_edge_co;
+ float *face_edge_no;
+ if (flip) {
+ face_edge_co = FACE_getIFCo(face, subdivLevels, S1, x - edgeSize / 2, gridSize - 1);
+ face_edge_no = FACE_getIFNo(face, subdivLevels, S1, x - edgeSize / 2, gridSize - 1);
+ }
+ else {
+ face_edge_co = FACE_getIFCo(face, subdivLevels, S1, gridSize - 1, x - edgeSize / 2);
+ face_edge_no = FACE_getIFNo(face, subdivLevels, S1, gridSize - 1, x - edgeSize / 2);
+ }
+ VertDataCopy(edge_co, face_edge_co, ss);
+ if (do_normals) {
+ VertDataCopy(edge_no, face_edge_no, ss);
+ }
+ }
+ }
+}
+
+static void opensubdiv_evaluateGrids(CCGSubSurf *ss)
+{
+ int i;
+ for (i = 0; i < ss->fMap->curSize; i++) {
+ CCGFace *face = (CCGFace *) ss->fMap->buckets[i];
+ for (; face; face = face->next) {
+ if (face->numVerts == 4) {
+ /* For quads we do special magic with converting face coords
+ * into corner coords and interpolating grids from it.
+ */
+ opensubdiv_evaluateQuadFaceGrids(ss, face, face->osd_index);
+ }
+ else {
+ /* NGons and tris are split into separate osd faces which
+ * evaluates onto grids directly.
+ */
+ opensubdiv_evaluateNGonFaceGrids(ss, face, face->osd_index);
+ }
+ }
+ }
+}
+
+CCGError ccgSubSurf_initOpenSubdivSync(CCGSubSurf *ss)
+{
+ if (ss->syncState != eSyncState_None) {
+ return eCCGError_InvalidSyncState;
+ }
+ ss->syncState = eSyncState_OpenSubdiv;
+ return eCCGError_None;
+}
+
+void ccgSubSurf_prepareTopologyRefiner(CCGSubSurf *ss, DerivedMesh *dm)
+{
+ if (ss->osd_mesh == NULL || ss->osd_mesh_invalid) {
+ if (dm->getNumPolys(dm) != 0) {
+ OpenSubdiv_Converter converter;
+ ccgSubSurf_converter_setup_from_derivedmesh(ss, dm, &converter);
+ /* TODO(sergey): Remove possibly previously allocated refiner. */
+ ss->osd_topology_refiner = openSubdiv_createTopologyRefinerDescr(&converter);
+ ccgSubSurf_converter_free(&converter);
+ }
+ }
+
+ /* Update number of grids, needed for things like final faces
+ * counter, used by display drawing.
+ */
+ {
+ const int num_polys = dm->getNumPolys(dm);
+ const MPoly *mpoly = dm->getPolyArray(dm);
+ int poly;
+ ss->numGrids = 0;
+ for (poly = 0; poly < num_polys; ++poly) {
+ ss->numGrids += mpoly[poly].totloop;
+ }
+ }
+
+ {
+ const int num_verts = dm->getNumVerts(dm);
+ const MVert *mvert = dm->getVertArray(dm);
+ int vert;
+ if (ss->osd_coarse_coords != NULL &&
+ num_verts != ss->osd_num_coarse_coords)
+ {
+ MEM_freeN(ss->osd_coarse_coords);
+ ss->osd_coarse_coords = NULL;
+ }
+ if (ss->osd_coarse_coords == NULL) {
+ ss->osd_coarse_coords = MEM_mallocN(sizeof(float) * 6 * num_verts, "osd coarse positions");
+ }
+ for (vert = 0; vert < num_verts; vert++) {
+ copy_v3_v3(ss->osd_coarse_coords[vert * 2 + 0], mvert[vert].co);
+ normal_short_to_float_v3(ss->osd_coarse_coords[vert * 2 + 1], mvert[vert].no);
+ }
+ ss->osd_num_coarse_coords = num_verts;
+ ss->osd_coarse_coords_invalid = true;
+ }
+}
+
+void ccgSubSurf__sync_opensubdiv(CCGSubSurf *ss)
+{
+ BLI_assert(ss->meshIFC.numLayers == 2 || ss->meshIFC.numLayers == 3);
+
+ /* Common synchronization steps */
+ ss->osd_compute = U.opensubdiv_compute_type;
+
+ if (ss->skip_grids == false) {
+ /* Make sure OSD evaluator is up-to-date. */
+ if (opensubdiv_ensureEvaluator(ss)) {
+ /* Update coarse points in the OpenSubdiv evaluator. */
+ opensubdiv_updateEvaluatorCoarsePositions(ss);
+
+ /* Evaluate opensubdiv mesh into the CCG grids. */
+ opensubdiv_evaluateGrids(ss);
+ }
+ }
+ else {
+ BLI_assert(ss->meshIFC.numLayers == 3);
+ }
+
+#ifdef DUMP_RESULT_GRIDS
+ ccgSubSurf__dumpCoords(ss);
+#endif
+}
+
+static const OpenSubdiv_TopologyRefinerDescr *get_effective_refiner(
+ const CCGSubSurf *ss)
+{
+ if (ss->osd_topology_refiner != NULL) {
+ return ss->osd_topology_refiner;
+ }
+ if (ss->osd_mesh != NULL) {
+ return openSubdiv_getGLMeshTopologyRefiner(ss->osd_mesh);
+ }
+ return 0;
+}
+
+int ccgSubSurf__getNumOsdBaseVerts(const CCGSubSurf *ss)
+{
+ const OpenSubdiv_TopologyRefinerDescr *topology_refiner =
+ get_effective_refiner(ss);
+ if (topology_refiner == NULL) {
+ return 0;
+ }
+ return openSubdiv_topologyRefinerGetNumVerts(topology_refiner);
+}
+
+int ccgSubSurf__getNumOsdBaseEdges(const CCGSubSurf *ss)
+{
+ const OpenSubdiv_TopologyRefinerDescr *topology_refiner =
+ get_effective_refiner(ss);
+ if (topology_refiner == NULL) {
+ return 0;
+ }
+ return openSubdiv_topologyRefinerGetNumEdges(topology_refiner);
+}
+
+int ccgSubSurf__getNumOsdBaseFaces(const CCGSubSurf *ss)
+{
+ const OpenSubdiv_TopologyRefinerDescr *topology_refiner =
+ get_effective_refiner(ss);
+ if (topology_refiner == NULL) {
+ return 0;
+ }
+ return openSubdiv_topologyRefinerGetNumFaces(topology_refiner);
+}
+
+#endif /* WITH_OPENSUBDIV */