diff options
Diffstat (limited to 'source/blender/blenkernel/intern')
67 files changed, 6790 insertions, 1069 deletions
diff --git a/source/blender/blenkernel/intern/CCGSubSurf.c b/source/blender/blenkernel/intern/CCGSubSurf.c index 81b1afa3621..dd5a87a445d 100644 --- a/source/blender/blenkernel/intern/CCGSubSurf.c +++ b/source/blender/blenkernel/intern/CCGSubSurf.c @@ -40,6 +40,8 @@ #ifdef WITH_OPENSUBDIV # include "opensubdiv_capi.h" # include "opensubdiv_converter_capi.h" +# include "opensubdiv_evaluator_capi.h" +# include "opensubdiv_topology_refiner_capi.h" #endif #include "GPU_glew.h" @@ -329,7 +331,7 @@ void ccgSubSurf_free(CCGSubSurf *ss) CCGAllocatorHDL allocator = ss->allocator; #ifdef WITH_OPENSUBDIV if (ss->osd_evaluator != NULL) { - openSubdiv_deleteEvaluatorDescr(ss->osd_evaluator); + openSubdiv_deleteEvaluator(ss->osd_evaluator); } if (ss->osd_mesh != NULL) { ccgSubSurf__delete_osdGLMesh(ss->osd_mesh); @@ -341,7 +343,7 @@ void ccgSubSurf_free(CCGSubSurf *ss) MEM_freeN(ss->osd_coarse_coords); } if (ss->osd_topology_refiner != NULL) { - openSubdiv_deleteTopologyRefinerDescr(ss->osd_topology_refiner); + openSubdiv_deleteTopologyRefiner(ss->osd_topology_refiner); } #endif diff --git a/source/blender/blenkernel/intern/CCGSubSurf_intern.h b/source/blender/blenkernel/intern/CCGSubSurf_intern.h index 9df1c9021ef..29e327d8973 100644 --- a/source/blender/blenkernel/intern/CCGSubSurf_intern.h +++ b/source/blender/blenkernel/intern/CCGSubSurf_intern.h @@ -236,7 +236,7 @@ struct CCGSubSurf { * Refiner is created from the modifier stack and used later from the main * thread to construct GL mesh to avoid threaded access to GL. */ - struct OpenSubdiv_TopologyRefinerDescr *osd_topology_refiner; /* Only used at synchronization stage. */ + struct OpenSubdiv_TopologyRefiner *osd_topology_refiner; /* Only used at synchronization stage. */ /* Denotes whether osd_mesh is invalid now due to topology changes and needs * to be reconstructed. * @@ -249,7 +249,7 @@ struct CCGSubSurf { /* ** CPU backend. ** */ /* Limit evaluator, used to evaluate CCG. */ - struct OpenSubdiv_EvaluatorDescr *osd_evaluator; + struct OpenSubdiv_Evaluator *osd_evaluator; /* Next PTex face index, used while CCG synchronization * to fill in PTex index of CCGFace. */ diff --git a/source/blender/blenkernel/intern/CCGSubSurf_opensubdiv.c b/source/blender/blenkernel/intern/CCGSubSurf_opensubdiv.c index 46204898709..98a17ad8009 100644 --- a/source/blender/blenkernel/intern/CCGSubSurf_opensubdiv.c +++ b/source/blender/blenkernel/intern/CCGSubSurf_opensubdiv.c @@ -42,6 +42,9 @@ #include "opensubdiv_capi.h" #include "opensubdiv_converter_capi.h" +#include "opensubdiv_evaluator_capi.h" +#include "opensubdiv_gl_mesh_capi.h" +#include "opensubdiv_topology_refiner_capi.h" #include "GPU_glew.h" #include "GPU_extensions.h" @@ -131,7 +134,6 @@ static bool compare_ccg_derivedmesh_topology(CCGSubSurf *ss, DerivedMesh *dm) 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) { @@ -140,15 +142,10 @@ static bool compare_osd_derivedmesh_topology(CCGSubSurf *ss, DerivedMesh *dm) /* 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); + result = openSubdiv_topologyRefinerCompareWithConverter( + ss->osd_topology_refiner, + &converter); ccgSubSurf_converter_free(&converter); return result; } @@ -159,22 +156,13 @@ static bool opensubdiv_is_topology_changed(CCGSubSurf *ss, DerivedMesh *dm) return true; } if (ss->osd_topology_refiner != NULL) { - int levels = openSubdiv_topologyRefinerGetSubdivLevel( + const int levels = ss->osd_topology_refiner->getSubdivisionLevel( 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; } @@ -194,13 +182,13 @@ void ccgSubSurf_checkTopologyChanged(CCGSubSurf *ss, DerivedMesh *dm) /* Reset GPU part. */ ss->osd_mesh_invalid = true; if (ss->osd_topology_refiner != NULL) { - openSubdiv_deleteTopologyRefinerDescr(ss->osd_topology_refiner); + openSubdiv_deleteTopologyRefiner(ss->osd_topology_refiner); ss->osd_topology_refiner = NULL; } /* Reset CPU side. */ if (ss->osd_evaluator != NULL) { - openSubdiv_deleteEvaluatorDescr(ss->osd_evaluator); + openSubdiv_deleteEvaluator(ss->osd_evaluator); ss->osd_evaluator = NULL; } } @@ -209,10 +197,10 @@ void ccgSubSurf_checkTopologyChanged(CCGSubSurf *ss, DerivedMesh *dm) 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); + ss->osd_mesh->setCoarsePositions(ss->osd_mesh, + (float *) ss->osd_coarse_coords, + 0, + ss->osd_num_coarse_coords); } bool ccgSubSurf_prepareGLMesh(CCGSubSurf *ss, @@ -259,9 +247,7 @@ bool ccgSubSurf_prepareGLMesh(CCGSubSurf *ss, ss->osd_mesh = openSubdiv_createOsdGLMeshFromTopologyRefiner( ss->osd_topology_refiner, - compute_type, - ss->subdivLevels); - ss->osd_topology_refiner = NULL; + compute_type); if (UNLIKELY(ss->osd_mesh == NULL)) { /* Most likely compute device is not available. */ @@ -269,13 +255,12 @@ bool ccgSubSurf_prepareGLMesh(CCGSubSurf *ss, } ccgSubSurf__updateGLMeshCoords(ss); - openSubdiv_osdGLMeshRefine(ss->osd_mesh); - openSubdiv_osdGLMeshSynchronize(ss->osd_mesh); + ss->osd_mesh->refine(ss->osd_mesh); + ss->osd_mesh->synchronize(ss->osd_mesh); ss->osd_coarse_coords_invalid = false; glBindVertexArray(ss->osd_vao); - glBindBuffer(GL_ARRAY_BUFFER, - openSubdiv_getOsdGLMeshVertexBuffer(ss->osd_mesh)); + ss->osd_mesh->bindVertexBuffer(ss->osd_mesh); glEnableVertexAttribArray(0); glEnableVertexAttribArray(1); @@ -289,12 +274,12 @@ bool ccgSubSurf_prepareGLMesh(CCGSubSurf *ss, } else if (ss->osd_coarse_coords_invalid) { ccgSubSurf__updateGLMeshCoords(ss); - openSubdiv_osdGLMeshRefine(ss->osd_mesh); - openSubdiv_osdGLMeshSynchronize(ss->osd_mesh); + ss->osd_mesh->refine(ss->osd_mesh); + ss->osd_mesh->synchronize(ss->osd_mesh); ss->osd_coarse_coords_invalid = false; } - openSubdiv_osdGLMeshDisplayPrepare(use_osd_glsl, active_uv_index); + ss->osd_mesh->prepareDraw(ss->osd_mesh, use_osd_glsl, active_uv_index); return true; } @@ -305,12 +290,12 @@ void ccgSubSurf_drawGLMesh(CCGSubSurf *ss, bool fill_quads, if (LIKELY(ss->osd_mesh != NULL)) { glBindVertexArray(ss->osd_vao); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, - openSubdiv_getOsdGLMeshPatchIndexBuffer(ss->osd_mesh)); + ss->osd_mesh->getPatchIndexBuffer(ss->osd_mesh)); - openSubdiv_osdGLMeshBindVertexBuffer(ss->osd_mesh); + ss->osd_mesh->bindVertexBuffer(ss->osd_mesh); glBindVertexArray(ss->osd_vao); - openSubdiv_osdGLMeshDisplay(ss->osd_mesh, fill_quads, - start_partition, num_partitions); + ss->osd_mesh->drawPatches(ss->osd_mesh, fill_quads, + start_partition, num_partitions); glBindVertexArray(0); glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); @@ -319,33 +304,21 @@ void ccgSubSurf_drawGLMesh(CCGSubSurf *ss, bool fill_quads, int ccgSubSurf_getNumGLMeshBaseFaces(CCGSubSurf *ss) { - const OpenSubdiv_TopologyRefinerDescr *topology_refiner; if (ss->osd_topology_refiner != NULL) { - topology_refiner = ss->osd_topology_refiner; - } - else if (ss->osd_mesh != NULL) { - topology_refiner = openSubdiv_getGLMeshTopologyRefiner(ss->osd_mesh); - } - else { - return 0; + return ss->osd_topology_refiner->getNumFaces( + ss->osd_topology_refiner); } - return openSubdiv_topologyRefinerGetNumFaces(topology_refiner); + return 0; } /* Get number of vertices in base faces in a particular GL mesh. */ int ccgSubSurf_getNumGLMeshBaseFaceVerts(CCGSubSurf *ss, int face) { - const OpenSubdiv_TopologyRefinerDescr *topology_refiner; if (ss->osd_topology_refiner != NULL) { - topology_refiner = ss->osd_topology_refiner; - } - else if (ss->osd_mesh != NULL) { - topology_refiner = openSubdiv_getGLMeshTopologyRefiner(ss->osd_mesh); - } - else { - return 0; + return ss->osd_topology_refiner->getNumFaceVertices( + ss->osd_topology_refiner, face); } - return openSubdiv_topologyRefinerGetNumFaceVerts(topology_refiner, face); + return 0; } void ccgSubSurf_setSkipGrids(CCGSubSurf *ss, bool skip_grids) @@ -453,17 +426,21 @@ void ccgSubSurf_evaluatorFVarUV(CCGSubSurf *ss, static bool opensubdiv_createEvaluator(CCGSubSurf *ss) { OpenSubdiv_Converter converter; - OpenSubdiv_TopologyRefinerDescr *topology_refiner; + OpenSubdiv_TopologyRefiner *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); + OpenSubdiv_TopologyRefinerSettings settings; + settings.level = ss->subdivLevels; + settings.is_adaptive = false; + topology_refiner = + openSubdiv_createTopologyRefinerFromConverter( + &converter, &settings); ccgSubSurf_converter_free(&converter); ss->osd_evaluator = - openSubdiv_createEvaluatorDescr(topology_refiner, - ss->subdivLevels); + openSubdiv_createEvaluatorFromTopologyRefiner(topology_refiner); if (ss->osd_evaluator == NULL) { BLI_assert(!"OpenSubdiv initialization failed, should not happen."); return false; @@ -519,11 +496,11 @@ static void opensubdiv_updateEvaluatorCoarsePositions(CCGSubSurf *ss) } } - openSubdiv_setEvaluatorCoarsePositions(ss->osd_evaluator, - (float *)positions, - 0, - num_basis_verts); - openSubdiv_refineEvaluator(ss->osd_evaluator); + ss->osd_evaluator->setCoarsePositions(ss->osd_evaluator, + (float *)positions, + 0, + num_basis_verts); + ss->osd_evaluator->refine(ss->osd_evaluator); MEM_freeN(positions); } @@ -558,11 +535,12 @@ static void opensubdiv_evaluateQuadFaceGrids(CCGSubSurf *ss, 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); + ss->osd_evaluator->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]); @@ -636,7 +614,11 @@ static void opensubdiv_evaluateQuadFaceGrids(CCGSubSurf *ss, * 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); + ss->osd_evaluator->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); @@ -700,7 +682,11 @@ static void opensubdiv_evaluateNGonFaceGrids(CCGSubSurf *ss, 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); + ss->osd_evaluator->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]); @@ -838,7 +824,12 @@ void ccgSubSurf_prepareTopologyRefiner(CCGSubSurf *ss, DerivedMesh *dm) 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); + OpenSubdiv_TopologyRefinerSettings settings; + settings.level = ss->subdivLevels; + settings.is_adaptive = false; + ss->osd_topology_refiner = + openSubdiv_createTopologyRefinerFromConverter( + &converter, &settings); ccgSubSurf_converter_free(&converter); } } diff --git a/source/blender/blenkernel/intern/CCGSubSurf_opensubdiv_converter.c b/source/blender/blenkernel/intern/CCGSubSurf_opensubdiv_converter.c index 8c1ba0c3782..649b7c7fa4c 100644 --- a/source/blender/blenkernel/intern/CCGSubSurf_opensubdiv_converter.c +++ b/source/blender/blenkernel/intern/CCGSubSurf_opensubdiv_converter.c @@ -389,12 +389,6 @@ static int conv_dm_get_num_uvs(const OpenSubdiv_Converter *converter) return storage->num_uvs; } -static void conv_dm_get_uvs(const OpenSubdiv_Converter *converter, float *uvs) -{ - ConvDMStorage *storage = converter->user_data; - memcpy(uvs, storage->uvs, sizeof(float) * 2 * storage->num_uvs); -} - static int conv_dm_get_face_corner_uv_index(const OpenSubdiv_Converter *converter, int face, int corner) @@ -432,35 +426,34 @@ void ccgSubSurf_converter_setup_from_derivedmesh( { ConvDMStorage *user_data; - converter->get_scheme_type = conv_dm_get_type; + converter->getSchemeType = conv_dm_get_type; - converter->get_fvar_linear_interpolation = + converter->getFVarLinearInterpolation = conv_dm_get_fvar_linear_interpolation; - converter->get_num_faces = conv_dm_get_num_faces; - converter->get_num_edges = conv_dm_get_num_edges; - converter->get_num_verts = conv_dm_get_num_verts; + converter->getNumFaces = conv_dm_get_num_faces; + converter->getNumEdges = conv_dm_get_num_edges; + converter->getNumVertices = conv_dm_get_num_verts; - converter->get_num_face_verts = conv_dm_get_num_face_verts; - converter->get_face_verts = conv_dm_get_face_verts; - converter->get_face_edges = conv_dm_get_face_edges; + converter->getNumFaceVertices = conv_dm_get_num_face_verts; + converter->getFaceVertices = conv_dm_get_face_verts; + converter->getFaceEdges = conv_dm_get_face_edges; - converter->get_edge_verts = conv_dm_get_edge_verts; - converter->get_num_edge_faces = conv_dm_get_num_edge_faces; - converter->get_edge_faces = conv_dm_get_edge_faces; - converter->get_edge_sharpness = conv_dm_get_edge_sharpness; + converter->getEdgeVertices = conv_dm_get_edge_verts; + converter->getNumEdgeFaces = conv_dm_get_num_edge_faces; + converter->getEdgeFaces = conv_dm_get_edge_faces; + converter->getEdgeSharpness = conv_dm_get_edge_sharpness; - converter->get_num_vert_edges = conv_dm_get_num_vert_edges; - converter->get_vert_edges = conv_dm_get_vert_edges; - converter->get_num_vert_faces = conv_dm_get_num_vert_faces; - converter->get_vert_faces = conv_dm_get_vert_faces; + converter->getNumVertexEdges = conv_dm_get_num_vert_edges; + converter->getVertexEdges = conv_dm_get_vert_edges; + converter->getNumVertexFaces = conv_dm_get_num_vert_faces; + converter->getVertexFaces = conv_dm_get_vert_faces; - converter->get_num_uv_layers = conv_dm_get_num_uv_layers; - converter->precalc_uv_layer = conv_dm_precalc_uv_layer; - converter->finish_uv_layer = conv_dm_finish_uv_layer; - converter->get_num_uvs = conv_dm_get_num_uvs; - converter->get_uvs = conv_dm_get_uvs; - converter->get_face_corner_uv_index = conv_dm_get_face_corner_uv_index; + converter->getNumUVLayers = conv_dm_get_num_uv_layers; + converter->precalcUVLayer = conv_dm_precalc_uv_layer; + converter->finishUVLayer = conv_dm_finish_uv_layer; + converter->getNumUVCoordinates = conv_dm_get_num_uvs; + converter->getFaceCornerUVIndex = conv_dm_get_face_corner_uv_index; user_data = MEM_mallocN(sizeof(ConvDMStorage), __func__); user_data->ss = ss; @@ -476,7 +469,7 @@ void ccgSubSurf_converter_setup_from_derivedmesh( user_data->uvs = NULL; user_data->face_uvs = NULL; - converter->free_user_data = conv_dm_free_user_data; + converter->freeUserData = conv_dm_free_user_data; converter->user_data = user_data; #ifdef USE_MESH_ELEMENT_MAPPING @@ -702,11 +695,6 @@ static int conv_ccg_get_num_uvs(const OpenSubdiv_Converter *UNUSED(converter)) return 0; } -static void conv_ccg_get_uvs(const OpenSubdiv_Converter * UNUSED(converter), - float *UNUSED(uvs)) -{ -} - static int conv_ccg_get_face_corner_uv_index(const OpenSubdiv_Converter *UNUSED(converter), int UNUSED(face), int UNUSED(corner_)) @@ -717,45 +705,44 @@ static int conv_ccg_get_face_corner_uv_index(const OpenSubdiv_Converter *UNUSED( void ccgSubSurf_converter_setup_from_ccg(CCGSubSurf *ss, OpenSubdiv_Converter *converter) { - converter->get_scheme_type = conv_ccg_get_bilinear_type; + converter->getSchemeType = conv_ccg_get_bilinear_type; - converter->get_fvar_linear_interpolation = + converter->getFVarLinearInterpolation = conv_ccg_get_fvar_linear_interpolation; - converter->get_num_faces = conv_ccg_get_num_faces; - converter->get_num_edges = conv_ccg_get_num_edges; - converter->get_num_verts = conv_ccg_get_num_verts; + converter->getNumFaces = conv_ccg_get_num_faces; + converter->getNumEdges = conv_ccg_get_num_edges; + converter->getNumVertices = conv_ccg_get_num_verts; - converter->get_num_face_verts = conv_ccg_get_num_face_verts; - converter->get_face_verts = conv_ccg_get_face_verts; - converter->get_face_edges = conv_ccg_get_face_edges; + converter->getNumFaceVertices = conv_ccg_get_num_face_verts; + converter->getFaceVertices = conv_ccg_get_face_verts; + converter->getFaceEdges = conv_ccg_get_face_edges; - converter->get_edge_verts = conv_ccg_get_edge_verts; - converter->get_num_edge_faces = conv_ccg_get_num_edge_faces; - converter->get_edge_faces = conv_ccg_get_edge_faces; - converter->get_edge_sharpness = conv_ccg_get_edge_sharpness; + converter->getEdgeVertices = conv_ccg_get_edge_verts; + converter->getNumEdgeFaces = conv_ccg_get_num_edge_faces; + converter->getEdgeFaces = conv_ccg_get_edge_faces; + converter->getEdgeSharpness = conv_ccg_get_edge_sharpness; - converter->get_num_vert_edges = conv_ccg_get_num_vert_edges; - converter->get_vert_edges = conv_ccg_get_vert_edges; - converter->get_num_vert_faces = conv_ccg_get_num_vert_faces; - converter->get_vert_faces = conv_ccg_get_vert_faces; + converter->getNumVertexEdges = conv_ccg_get_num_vert_edges; + converter->getVertexEdges = conv_ccg_get_vert_edges; + converter->getNumVertexFaces = conv_ccg_get_num_vert_faces; + converter->getVertexFaces = conv_ccg_get_vert_faces; - converter->get_num_uv_layers = conv_ccg_get_num_uv_layers; - converter->precalc_uv_layer = conv_ccg_precalc_uv_layer; - converter->finish_uv_layer = conv_ccg_finish_uv_layer; - converter->get_num_uvs = conv_ccg_get_num_uvs; - converter->get_uvs = conv_ccg_get_uvs; - converter->get_face_corner_uv_index = conv_ccg_get_face_corner_uv_index; + converter->getNumUVLayers = conv_ccg_get_num_uv_layers; + converter->precalcUVLayer = conv_ccg_precalc_uv_layer; + converter->finishUVLayer = conv_ccg_finish_uv_layer; + converter->getNumUVCoordinates = conv_ccg_get_num_uvs; + converter->getFaceCornerUVIndex = conv_ccg_get_face_corner_uv_index; - converter->free_user_data = NULL; + converter->freeUserData = NULL; converter->user_data = ss; } void ccgSubSurf_converter_free( struct OpenSubdiv_Converter *converter) { - if (converter->free_user_data) { - converter->free_user_data(converter); + if (converter->freeUserData) { + converter->freeUserData(converter); } } diff --git a/source/blender/blenkernel/intern/DerivedMesh.c b/source/blender/blenkernel/intern/DerivedMesh.c index 9c4aae7cda5..05253f7962a 100644 --- a/source/blender/blenkernel/intern/DerivedMesh.c +++ b/source/blender/blenkernel/intern/DerivedMesh.c @@ -2098,6 +2098,7 @@ static void mesh_calc_modifiers( /* XXX: Is build_shapekey_layers ever even true? This should have crashed long ago... */ BLI_assert(!build_shapekey_layers); + UNUSED_VARS_NDEBUG(build_shapekey_layers); //if (build_shapekey_layers) // add_shapekey_layers(*r_deform_mesh, me, ob); diff --git a/source/blender/blenkernel/intern/action.c b/source/blender/blenkernel/intern/action.c index cbdabe2c440..6f97187f1e3 100644 --- a/source/blender/blenkernel/intern/action.c +++ b/source/blender/blenkernel/intern/action.c @@ -1328,6 +1328,37 @@ void BKE_pose_rest(bPose *pose) } } +void BKE_pose_copyesult_pchan_result(bPoseChannel *pchanto, const bPoseChannel *pchanfrom) +{ + copy_m4_m4(pchanto->pose_mat, pchanfrom->pose_mat); + copy_m4_m4(pchanto->chan_mat, pchanfrom->chan_mat); + + /* used for local constraints */ + copy_v3_v3(pchanto->loc, pchanfrom->loc); + copy_qt_qt(pchanto->quat, pchanfrom->quat); + copy_v3_v3(pchanto->eul, pchanfrom->eul); + copy_v3_v3(pchanto->size, pchanfrom->size); + + copy_v3_v3(pchanto->pose_head, pchanfrom->pose_head); + copy_v3_v3(pchanto->pose_tail, pchanfrom->pose_tail); + + pchanto->roll1 = pchanfrom->roll1; + pchanto->roll2 = pchanfrom->roll2; + pchanto->curveInX = pchanfrom->curveInX; + pchanto->curveInY = pchanfrom->curveInY; + pchanto->curveOutX = pchanfrom->curveOutX; + pchanto->curveOutY = pchanfrom->curveOutY; + pchanto->ease1 = pchanfrom->ease1; + pchanto->ease2 = pchanfrom->ease2; + pchanto->scaleIn = pchanfrom->scaleIn; + pchanto->scaleOut = pchanfrom->scaleOut; + + pchanto->rotmode = pchanfrom->rotmode; + pchanto->flag = pchanfrom->flag; + pchanto->protectflag = pchanfrom->protectflag; + pchanto->bboneflag = pchanfrom->bboneflag; +} + /* both poses should be in sync */ bool BKE_pose_copy_result(bPose *to, bPose *from) { @@ -1346,34 +1377,8 @@ bool BKE_pose_copy_result(bPose *to, bPose *from) for (pchanfrom = from->chanbase.first; pchanfrom; pchanfrom = pchanfrom->next) { pchanto = BKE_pose_channel_find_name(to, pchanfrom->name); - if (pchanto) { - copy_m4_m4(pchanto->pose_mat, pchanfrom->pose_mat); - copy_m4_m4(pchanto->chan_mat, pchanfrom->chan_mat); - - /* used for local constraints */ - copy_v3_v3(pchanto->loc, pchanfrom->loc); - copy_qt_qt(pchanto->quat, pchanfrom->quat); - copy_v3_v3(pchanto->eul, pchanfrom->eul); - copy_v3_v3(pchanto->size, pchanfrom->size); - - copy_v3_v3(pchanto->pose_head, pchanfrom->pose_head); - copy_v3_v3(pchanto->pose_tail, pchanfrom->pose_tail); - - pchanto->roll1 = pchanfrom->roll1; - pchanto->roll2 = pchanfrom->roll2; - pchanto->curveInX = pchanfrom->curveInX; - pchanto->curveInY = pchanfrom->curveInY; - pchanto->curveOutX = pchanfrom->curveOutX; - pchanto->curveOutY = pchanfrom->curveOutY; - pchanto->ease1 = pchanfrom->ease1; - pchanto->ease2 = pchanfrom->ease2; - pchanto->scaleIn = pchanfrom->scaleIn; - pchanto->scaleOut = pchanfrom->scaleOut; - - pchanto->rotmode = pchanfrom->rotmode; - pchanto->flag = pchanfrom->flag; - pchanto->protectflag = pchanfrom->protectflag; - pchanto->bboneflag = pchanfrom->bboneflag; + if (pchanto != NULL) { + BKE_pose_copyesult_pchan_result(pchanto, pchanfrom); } } return true; diff --git a/source/blender/blenkernel/intern/anim.c b/source/blender/blenkernel/intern/anim.c index 07b8b69bc70..7df889d22b2 100644 --- a/source/blender/blenkernel/intern/anim.c +++ b/source/blender/blenkernel/intern/anim.c @@ -99,6 +99,8 @@ void animviz_settings_init(bAnimVizSettings *avs) avs->path_viewflag = (MOTIONPATH_VIEW_KFRAS | MOTIONPATH_VIEW_KFNOS); avs->path_step = 1; + + avs->path_bakeflag |= MOTIONPATH_BAKE_HEADS; } /* ------------------- */ @@ -114,9 +116,9 @@ void animviz_free_motionpath_cache(bMotionPath *mpath) if (mpath->points) MEM_freeN(mpath->points); - GWN_VERTBUF_DISCARD_SAFE(mpath->points_vbo); - GWN_BATCH_DISCARD_SAFE(mpath->batch_line); - GWN_BATCH_DISCARD_SAFE(mpath->batch_points); + GPU_VERTBUF_DISCARD_SAFE(mpath->points_vbo); + GPU_BATCH_DISCARD_SAFE(mpath->batch_line); + GPU_BATCH_DISCARD_SAFE(mpath->batch_points); /* reset the relevant parameters */ mpath->points = NULL; @@ -495,9 +497,9 @@ static void motionpaths_calc_bake_targets(Scene *scene, ListBase *targets) BLI_dlrbTree_free(&mpt->keys); /* Free previous batches to force update. */ - GWN_VERTBUF_DISCARD_SAFE(mpath->points_vbo); - GWN_BATCH_DISCARD_SAFE(mpath->batch_line); - GWN_BATCH_DISCARD_SAFE(mpath->batch_points); + GPU_VERTBUF_DISCARD_SAFE(mpath->points_vbo); + GPU_BATCH_DISCARD_SAFE(mpath->batch_line); + GPU_BATCH_DISCARD_SAFE(mpath->batch_points); } } @@ -535,18 +537,18 @@ void calc_curvepath(Object *ob, ListBase *nurbs) return; } - if (ob->curve_cache->path) free_path(ob->curve_cache->path); - ob->curve_cache->path = NULL; + if (ob->runtime.curve_cache->path) free_path(ob->runtime.curve_cache->path); + ob->runtime.curve_cache->path = NULL; /* weak! can only use first curve */ - bl = ob->curve_cache->bev.first; + bl = ob->runtime.curve_cache->bev.first; if (bl == NULL || !bl->nr) { return; } nu = nurbs->first; - ob->curve_cache->path = path = MEM_callocN(sizeof(Path), "calc_curvepath"); + ob->runtime.curve_cache->path = path = MEM_callocN(sizeof(Path), "calc_curvepath"); /* if POLY: last vertice != first vertice */ cycl = (bl->poly != -1); @@ -663,15 +665,15 @@ int where_on_path(Object *ob, float ctime, float vec[4], float dir[3], float qua if (ob == NULL || ob->type != OB_CURVE) return 0; cu = ob->data; - if (ob->curve_cache == NULL || ob->curve_cache->path == NULL || ob->curve_cache->path->data == NULL) { + if (ob->runtime.curve_cache == NULL || ob->runtime.curve_cache->path == NULL || ob->runtime.curve_cache->path->data == NULL) { printf("no path!\n"); return 0; } - path = ob->curve_cache->path; + path = ob->runtime.curve_cache->path; pp = path->data; /* test for cyclic */ - bl = ob->curve_cache->bev.first; + bl = ob->runtime.curve_cache->bev.first; if (!bl) return 0; if (!bl->nr) return 0; if (bl->poly > -1) cycl = 1; diff --git a/source/blender/blenkernel/intern/anim_sys.c b/source/blender/blenkernel/intern/anim_sys.c index fd7497f9ba1..b02b6b6ce15 100644 --- a/source/blender/blenkernel/intern/anim_sys.c +++ b/source/blender/blenkernel/intern/anim_sys.c @@ -1150,6 +1150,9 @@ void BKE_animdata_main_cb(Main *bmain, ID_AnimData_Edit_Callback func, void *use /* grease pencil */ ANIMDATA_IDS_CB(bmain->gpencil.first); + /* palettes */ + ANIMDATA_IDS_CB(bmain->palettes.first); + /* cache files */ ANIMDATA_IDS_CB(bmain->cachefiles.first); } @@ -2925,6 +2928,9 @@ void BKE_animsys_evaluate_all_animation(Main *main, Depsgraph *depsgraph, Scene /* grease pencil */ EVAL_ANIM_IDS(main->gpencil.first, ADT_RECALC_ANIM); + /* palettes */ + EVAL_ANIM_IDS(main->palettes.first, ADT_RECALC_ANIM); + /* cache files */ EVAL_ANIM_IDS(main->cachefiles.first, ADT_RECALC_ANIM); diff --git a/source/blender/blenkernel/intern/armature.c b/source/blender/blenkernel/intern/armature.c index bd9ee7c9e5f..cea81a82f4b 100644 --- a/source/blender/blenkernel/intern/armature.c +++ b/source/blender/blenkernel/intern/armature.c @@ -71,6 +71,8 @@ #include "BKE_object.h" #include "BKE_scene.h" +#include "DEG_depsgraph_build.h" + #include "BIK_api.h" /* **************** Generic Functions, data level *************** */ @@ -1951,9 +1953,14 @@ void BKE_pose_remap_bone_pointers(bArmature *armature, bPose *pose) BLI_ghash_free(bone_hash, NULL, NULL); } -/* only after leave editmode, duplicating, validating older files, library syncing */ -/* NOTE: pose->flag is set for it */ -void BKE_pose_rebuild(Object *ob, bArmature *arm) +/** + * Only after leave editmode, duplicating, validating older files, library syncing. + * + * \note pose->flag is set for it. + * + * \param bmain May be NULL, only used to tag depsgraph as being dirty... + */ +void BKE_pose_rebuild(Main *bmain, Object *ob, bArmature *arm, const bool do_id_user) { Bone *bone; bPose *pose; @@ -1982,7 +1989,7 @@ void BKE_pose_rebuild(Object *ob, bArmature *arm) for (pchan = pose->chanbase.first; pchan; pchan = next) { next = pchan->next; if (pchan->bone == NULL) { - BKE_pose_channel_free(pchan); + BKE_pose_channel_free_ex(pchan, do_id_user); BKE_pose_channels_hash_free(pose); BLI_freelinkN(&pose->chanbase, pchan); } @@ -1998,12 +2005,17 @@ void BKE_pose_rebuild(Object *ob, bArmature *arm) pose_proxy_synchronize(ob, ob->proxy, arm->layer_protected); } - BKE_pose_update_constraint_flags(ob->pose); /* for IK detection for example */ + BKE_pose_update_constraint_flags(pose); /* for IK detection for example */ + + pose->flag &= ~POSE_RECALC; + pose->flag |= POSE_WAS_REBUILT; - ob->pose->flag &= ~POSE_RECALC; - ob->pose->flag |= POSE_WAS_REBUILT; + BKE_pose_channels_hash_make(pose); - BKE_pose_channels_hash_make(ob->pose); + /* Rebuilding poses forces us to also rebuild the dependency graph, since there is one node per pose/bone... */ + if (bmain != NULL) { + DEG_relations_tag_update(bmain); + } } /* ********************** THE POSE SOLVER ******************* */ @@ -2277,8 +2289,10 @@ void BKE_pose_where_is(struct Depsgraph *depsgraph, Scene *scene, Object *ob) if (ELEM(NULL, arm, scene)) return; - if ((ob->pose == NULL) || (ob->pose->flag & POSE_RECALC)) - BKE_pose_rebuild(ob, arm); + if ((ob->pose == NULL) || (ob->pose->flag & POSE_RECALC)) { + /* WARNING! passing NULL bmain here means we won't tag depsgraph's as dirty - hopefully this is OK. */ + BKE_pose_rebuild(NULL, ob, arm, true); + } ctime = BKE_scene_frame_get(scene); /* not accurate... */ diff --git a/source/blender/blenkernel/intern/armature_update.c b/source/blender/blenkernel/intern/armature_update.c index 628f92c7803..d53c61255fe 100644 --- a/source/blender/blenkernel/intern/armature_update.c +++ b/source/blender/blenkernel/intern/armature_update.c @@ -201,7 +201,7 @@ static void splineik_init_tree_from_pchan(Scene *scene, Object *UNUSED(ob), bPos /* get the current length of the curve */ /* NOTE: this is assumed to be correct even after the curve was resized */ - splineLen = ikData->tar->curve_cache->path->totdist; + splineLen = ikData->tar->runtime.curve_cache->path->totdist; /* calculate the scale factor to multiply all the path values by so that the * bone chain retains its current length, such that @@ -558,6 +558,17 @@ void BKE_splineik_execute_tree( /* *************** Depsgraph evaluation callbacks ************ */ +static void pose_pchan_index_create(bPose *pose) +{ + const int num_channels = BLI_listbase_count(&pose->chanbase); + pose->chan_array = MEM_malloc_arrayN( + num_channels, sizeof(bPoseChannel *), "pose->chan_array"); + int pchan_index = 0; + for (bPoseChannel *pchan = pose->chanbase.first; pchan != NULL; pchan = pchan->next) { + pose->chan_array[pchan_index++] = pchan; + } +} + BLI_INLINE bPoseChannel *pose_pchan_get_indexed(Object *ob, int pchan_index) { bPose *pose = ob->pose; @@ -585,16 +596,12 @@ void BKE_pose_eval_init(struct Depsgraph *depsgraph, /* imat is needed for solvers. */ invert_m4_m4(ob->imat, ob->obmat); - const int num_channels = BLI_listbase_count(&pose->chanbase); - pose->chan_array = MEM_malloc_arrayN( - num_channels, sizeof(bPoseChannel *), "pose->chan_array"); - /* clear flags */ - int pchan_index = 0; for (bPoseChannel *pchan = pose->chanbase.first; pchan != NULL; pchan = pchan->next) { pchan->flag &= ~(POSE_DONE | POSE_CHAIN | POSE_IKTREE | POSE_IKSPLINE); - pose->chan_array[pchan_index++] = pchan; } + + pose_pchan_index_create(pose); } void BKE_pose_eval_init_ik(struct Depsgraph *depsgraph, @@ -688,7 +695,8 @@ void BKE_pose_bone_done(struct Depsgraph *depsgraph, invert_m4_m4(imat, pchan->bone->arm_mat); mul_m4_m4m4(pchan->chan_mat, pchan->pose_mat, imat); } - if (DEG_is_active(depsgraph)) { + bArmature *arm = (bArmature *)ob->data; + if (DEG_is_active(depsgraph) && arm->edbo == NULL) { bPoseChannel *pchan_orig = pchan->orig_pchan; copy_m4_m4(pchan_orig->pose_mat, pchan->pose_mat); copy_m4_m4(pchan_orig->chan_mat, pchan->chan_mat); @@ -751,12 +759,44 @@ void BKE_pose_eval_flush(struct Depsgraph *depsgraph, pose->chan_array = NULL; } -void BKE_pose_eval_proxy_copy(struct Depsgraph *depsgraph, Object *ob) +void BKE_pose_eval_proxy_pose_init(struct Depsgraph *depsgraph, Object *object) { - BLI_assert(ID_IS_LINKED(ob) && ob->proxy_from != NULL); - DEG_debug_print_eval(depsgraph, __func__, ob->id.name, ob); - if (BKE_pose_copy_result(ob->pose, ob->proxy_from->pose) == false) { - printf("Proxy copy error, lib Object: %s proxy Object: %s\n", - ob->id.name + 2, ob->proxy_from->id.name + 2); - } + BLI_assert(ID_IS_LINKED(object) && object->proxy_from != NULL); + DEG_debug_print_eval(depsgraph, __func__, object->id.name, object); + + pose_pchan_index_create(object->pose); +} + +void BKE_pose_eval_proxy_pose_done(struct Depsgraph *depsgraph, Object *object) +{ + BLI_assert(ID_IS_LINKED(object) && object->proxy_from != NULL); + DEG_debug_print_eval(depsgraph, __func__, object->id.name, object); + + bPose *pose = object->pose; + BLI_assert(pose->chan_array != NULL); + MEM_freeN(pose->chan_array); + pose->chan_array = NULL; +} + +void BKE_pose_eval_proxy_copy_bone( + struct Depsgraph *depsgraph, + Object *object, + int pchan_index) +{ + BLI_assert(ID_IS_LINKED(object) && object->proxy_from != NULL); + DEG_debug_print_eval(depsgraph, __func__, object->id.name, object); + bPoseChannel *pchan = pose_pchan_get_indexed(object, pchan_index); + /* TODO(sergey): Use indexec lookup, once it's guaranteed to be kept + * around for the time while proxies are evaluating. + */ +#if 0 + bPoseChannel *pchan_from = pose_pchan_get_indexed( + object->proxy_from, pchan_index); +#else + bPoseChannel *pchan_from = BKE_pose_channel_find_name( + object->proxy_from->pose, pchan->name); +#endif + BLI_assert(pchan != NULL); + BLI_assert(pchan_from != NULL); + BKE_pose_copyesult_pchan_result(pchan, pchan_from); } diff --git a/source/blender/blenkernel/intern/blender_user_menu.c b/source/blender/blenkernel/intern/blender_user_menu.c index 3ec46e23cd1..2c18de70e6d 100644 --- a/source/blender/blenkernel/intern/blender_user_menu.c +++ b/source/blender/blenkernel/intern/blender_user_menu.c @@ -89,6 +89,7 @@ bUserMenuItem *BKE_blender_user_menu_item_add(ListBase *lb, int type) size = sizeof(bUserMenuItem_Prop); } else { + size = sizeof(bUserMenuItem); BLI_assert(0); } diff --git a/source/blender/blenkernel/intern/brush.c b/source/blender/blenkernel/intern/brush.c index 42cd7968321..e1cc3984601 100644 --- a/source/blender/blenkernel/intern/brush.c +++ b/source/blender/blenkernel/intern/brush.c @@ -29,6 +29,7 @@ #include "DNA_brush_types.h" #include "DNA_scene_types.h" #include "DNA_object_types.h" +#include "DNA_gpencil_types.h" #include "BLI_math.h" #include "BLI_blenlib.h" @@ -36,6 +37,7 @@ #include "BKE_brush.h" #include "BKE_colortools.h" +#include "BKE_context.h" #include "BKE_global.h" #include "BKE_library.h" #include "BKE_library_query.h" @@ -129,6 +131,7 @@ static void brush_defaults(Brush *brush) brush->stencil_dimension[0] = 256; brush->stencil_dimension[1] = 256; + } /* Datablock add/copy/free/make_local */ @@ -164,6 +167,376 @@ Brush *BKE_brush_add(Main *bmain, const char *name, const eObjectMode ob_mode) return brush; } +/* add grese pencil settings */ +void BKE_brush_init_gpencil_settings(Brush *brush) +{ + if (brush->gpencil_settings == NULL) { + brush->gpencil_settings = MEM_callocN(sizeof(BrushGpencilSettings), "BrushGpencilSettings"); + } + + brush->gpencil_settings->draw_smoothlvl = 1; + brush->gpencil_settings->flag = 0; + brush->gpencil_settings->flag |= GP_BRUSH_USE_PRESSURE; + brush->gpencil_settings->draw_sensitivity = 1.0f; + brush->gpencil_settings->draw_strength = 1.0f; + brush->gpencil_settings->draw_jitter = 0.0f; + brush->gpencil_settings->flag |= GP_BRUSH_USE_JITTER_PRESSURE; + brush->gpencil_settings->icon_id = GP_BRUSH_ICON_PEN; + + /* curves */ + brush->gpencil_settings->curve_sensitivity = curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); + brush->gpencil_settings->curve_strength = curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); + brush->gpencil_settings->curve_jitter = curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); +} + +/* add a new gp-brush */ +Brush *BKE_brush_add_gpencil(Main *bmain, ToolSettings *ts, const char *name) +{ + Brush *brush; + Paint *paint = BKE_brush_get_gpencil_paint(ts); + brush = BKE_brush_add(bmain, name, OB_MODE_GPENCIL_PAINT); + + BKE_paint_brush_set(paint, brush); + id_us_min(&brush->id); + + brush->size = 3; + + /* grease pencil basic settings */ + BKE_brush_init_gpencil_settings(brush); + + /* return brush */ + return brush; +} + +Paint *BKE_brush_get_gpencil_paint(ToolSettings *ts) +{ + /* alloc paint session */ + if (ts->gp_paint == NULL) { + ts->gp_paint = MEM_callocN(sizeof(GpPaint), "GpPaint"); + } + + return &ts->gp_paint->paint; +} + +/* grease pencil cumapping->preset */ +typedef enum eGPCurveMappingPreset { + GPCURVE_PRESET_PENCIL = 0, + GPCURVE_PRESET_INK = 1, + GPCURVE_PRESET_INKNOISE = 2, +} eGPCurveMappingPreset; + +static void brush_gpencil_curvemap_reset(CurveMap *cuma, int preset) +{ + if (cuma->curve) + MEM_freeN(cuma->curve); + + cuma->totpoint = 3; + cuma->curve = MEM_callocN(cuma->totpoint * sizeof(CurveMapPoint), __func__); + + switch (preset) { + case GPCURVE_PRESET_PENCIL: + cuma->curve[0].x = 0.0f; + cuma->curve[0].y = 0.0f; + cuma->curve[1].x = 0.75115f; + cuma->curve[1].y = 0.25f; + cuma->curve[2].x = 1.0f; + cuma->curve[2].y = 1.0f; + break; + case GPCURVE_PRESET_INK: + cuma->curve[0].x = 0.0f; + cuma->curve[0].y = 0.0f; + cuma->curve[1].x = 0.63448f; + cuma->curve[1].y = 0.375f; + cuma->curve[2].x = 1.0f; + cuma->curve[2].y = 1.0f; + break; + case GPCURVE_PRESET_INKNOISE: + cuma->curve[0].x = 0.0f; + cuma->curve[0].y = 0.0f; + cuma->curve[1].x = 0.63134f; + cuma->curve[1].y = 0.3625f; + cuma->curve[2].x = 1.0f; + cuma->curve[2].y = 1.0f; + break; + } + + if (cuma->table) { + MEM_freeN(cuma->table); + cuma->table = NULL; + } +} + +/* create a set of grease pencil presets */ +void BKE_brush_gpencil_presets(bContext *C) +{ +#define SMOOTH_STROKE_RADIUS 40 +#define SMOOTH_STROKE_FACTOR 0.9f + + ToolSettings *ts = CTX_data_tool_settings(C); + Paint *paint = BKE_brush_get_gpencil_paint(ts); + Main *bmain = CTX_data_main(C); + + Brush *brush, *deft; + CurveMapping *custom_curve; + + /* Pencil brush */ + brush = BKE_brush_add_gpencil(bmain, ts, "Draw Pencil"); + brush->size = 25.0f; + brush->gpencil_settings->flag |= (GP_BRUSH_USE_PRESSURE | GP_BRUSH_ENABLE_CURSOR); + brush->gpencil_settings->draw_sensitivity = 1.0f; + + brush->gpencil_settings->draw_strength = 0.6f; + brush->gpencil_settings->flag |= GP_BRUSH_USE_STENGTH_PRESSURE; + + brush->gpencil_settings->draw_random_press = 0.0f; + + brush->gpencil_settings->draw_jitter = 0.0f; + brush->gpencil_settings->flag |= GP_BRUSH_USE_JITTER_PRESSURE; + + brush->gpencil_settings->draw_angle = 0.0f; + brush->gpencil_settings->draw_angle_factor = 0.0f; + + brush->gpencil_settings->flag |= GP_BRUSH_GROUP_SETTINGS; + brush->gpencil_settings->draw_smoothfac = 0.1f; + brush->gpencil_settings->draw_smoothlvl = 1; + brush->gpencil_settings->thick_smoothfac = 1.0f; + brush->gpencil_settings->thick_smoothlvl = 3; + brush->gpencil_settings->draw_subdivide = 1; + brush->gpencil_settings->draw_random_sub = 0.0f; + brush->gpencil_settings->icon_id = GP_BRUSH_ICON_PENCIL; + brush->gpencil_settings->brush_type = GP_BRUSH_TYPE_DRAW; + + brush->smooth_stroke_radius = SMOOTH_STROKE_RADIUS; + brush->smooth_stroke_factor = SMOOTH_STROKE_FACTOR; + + /* Pen brush */ + brush = BKE_brush_add_gpencil(bmain, ts, "Draw Pen"); + deft = brush; /* save default brush */ + brush->size = 30.0f; + brush->gpencil_settings->flag |= (GP_BRUSH_USE_PRESSURE | GP_BRUSH_ENABLE_CURSOR); + brush->gpencil_settings->draw_sensitivity = 1.0f; + + brush->gpencil_settings->draw_strength = 1.0f; + brush->gpencil_settings->flag |= GP_BRUSH_USE_STENGTH_PRESSURE; + + brush->gpencil_settings->draw_random_press = 0.0f; + brush->gpencil_settings->draw_random_strength = 0.0f; + + brush->gpencil_settings->draw_jitter = 0.0f; + brush->gpencil_settings->flag |= GP_BRUSH_USE_JITTER_PRESSURE; + + brush->gpencil_settings->draw_angle = 0.0f; + brush->gpencil_settings->draw_angle_factor = 0.0f; + + brush->gpencil_settings->flag |= GP_BRUSH_GROUP_SETTINGS; + brush->gpencil_settings->draw_smoothfac = 0.1f; + brush->gpencil_settings->draw_smoothlvl = 1; + brush->gpencil_settings->draw_subdivide = 1; + brush->gpencil_settings->thick_smoothfac = 1.0f; + brush->gpencil_settings->thick_smoothlvl = 3; + brush->gpencil_settings->draw_random_sub = 0.0f; + brush->gpencil_settings->icon_id = GP_BRUSH_ICON_PEN; + brush->gpencil_settings->brush_type = GP_BRUSH_TYPE_DRAW; + + brush->smooth_stroke_radius = SMOOTH_STROKE_RADIUS; + brush->smooth_stroke_factor = SMOOTH_STROKE_FACTOR; + + /* Ink brush */ + brush = BKE_brush_add_gpencil(bmain, ts, "Draw Ink"); + brush->size = 60.0f; + brush->gpencil_settings->flag |= (GP_BRUSH_USE_PRESSURE | GP_BRUSH_ENABLE_CURSOR); + brush->gpencil_settings->draw_sensitivity = 1.6f; + + brush->gpencil_settings->draw_strength = 1.0f; + + brush->gpencil_settings->draw_random_press = 0.0f; + + brush->gpencil_settings->draw_jitter = 0.0f; + brush->gpencil_settings->flag |= GP_BRUSH_USE_JITTER_PRESSURE; + + brush->gpencil_settings->draw_angle = 0.0f; + brush->gpencil_settings->draw_angle_factor = 0.0f; + + brush->gpencil_settings->flag |= GP_BRUSH_GROUP_SETTINGS; + brush->gpencil_settings->draw_smoothfac = 0.1f; + brush->gpencil_settings->draw_smoothlvl = 1; + brush->gpencil_settings->thick_smoothfac = 1.0f; + brush->gpencil_settings->thick_smoothlvl = 3; + brush->gpencil_settings->draw_subdivide = 1; + brush->gpencil_settings->draw_random_sub = 0.0f; + brush->gpencil_settings->icon_id = GP_BRUSH_ICON_INK; + brush->gpencil_settings->brush_type = GP_BRUSH_TYPE_DRAW; + + brush->smooth_stroke_radius = SMOOTH_STROKE_RADIUS; + brush->smooth_stroke_factor = SMOOTH_STROKE_FACTOR; + + /* Curve */ + custom_curve = brush->gpencil_settings->curve_sensitivity; + curvemapping_set_defaults(custom_curve, 0, 0.0f, 0.0f, 1.0f, 1.0f); + curvemapping_initialize(custom_curve); + brush_gpencil_curvemap_reset(custom_curve->cm, GPCURVE_PRESET_INK); + + /* Ink Noise brush */ + brush = BKE_brush_add_gpencil(bmain, ts, "Draw Noise"); + brush->size = 60.0f; + brush->gpencil_settings->flag |= (GP_BRUSH_USE_PRESSURE | GP_BRUSH_ENABLE_CURSOR); + brush->gpencil_settings->draw_sensitivity = 1.0f; + + brush->gpencil_settings->draw_strength = 1.0f; + + brush->gpencil_settings->flag |= GP_BRUSH_GROUP_RANDOM; + brush->gpencil_settings->draw_random_press = 0.7f; + brush->gpencil_settings->draw_random_strength = 0.0f; + + brush->gpencil_settings->draw_jitter = 0.0f; + brush->gpencil_settings->flag |= GP_BRUSH_USE_JITTER_PRESSURE; + + brush->gpencil_settings->draw_angle = 0.0f; + brush->gpencil_settings->draw_angle_factor = 0.0f; + + brush->gpencil_settings->flag |= GP_BRUSH_GROUP_SETTINGS; + brush->gpencil_settings->draw_smoothfac = 0.1f; + brush->gpencil_settings->draw_smoothlvl = 2; + brush->gpencil_settings->thick_smoothfac = 0.5f; + brush->gpencil_settings->thick_smoothlvl = 2; + brush->gpencil_settings->draw_subdivide = 1; + brush->gpencil_settings->draw_random_sub = 0.0f; + brush->gpencil_settings->icon_id = GP_BRUSH_ICON_INKNOISE; + brush->gpencil_settings->brush_type = GP_BRUSH_TYPE_DRAW; + + brush->smooth_stroke_radius = SMOOTH_STROKE_RADIUS; + brush->smooth_stroke_factor = SMOOTH_STROKE_FACTOR; + + /* Curve */ + custom_curve = brush->gpencil_settings->curve_sensitivity; + curvemapping_set_defaults(custom_curve, 0, 0.0f, 0.0f, 1.0f, 1.0f); + curvemapping_initialize(custom_curve); + brush_gpencil_curvemap_reset(custom_curve->cm, GPCURVE_PRESET_INKNOISE); + + /* Block Basic brush */ + brush = BKE_brush_add_gpencil(bmain, ts, "Draw Block"); + brush->size = 150.0f; + brush->gpencil_settings->flag |= (GP_BRUSH_USE_PRESSURE | GP_BRUSH_ENABLE_CURSOR); + brush->gpencil_settings->draw_sensitivity = 1.0f; + + brush->gpencil_settings->draw_strength = 0.7f; + brush->gpencil_settings->flag |= GP_BRUSH_USE_STENGTH_PRESSURE; + + brush->gpencil_settings->draw_random_press = 0.0f; + + brush->gpencil_settings->draw_jitter = 0.0f; + brush->gpencil_settings->flag |= GP_BRUSH_USE_JITTER_PRESSURE; + + brush->gpencil_settings->draw_angle = 0.0f; + brush->gpencil_settings->draw_angle_factor = 0.0f; + + brush->gpencil_settings->flag |= GP_BRUSH_GROUP_SETTINGS; + brush->gpencil_settings->draw_smoothfac = 0.0f; + brush->gpencil_settings->draw_smoothlvl = 1; + brush->gpencil_settings->thick_smoothfac = 1.0f; + brush->gpencil_settings->thick_smoothlvl = 3; + brush->gpencil_settings->draw_subdivide = 0; + brush->gpencil_settings->draw_random_sub = 0; + brush->gpencil_settings->icon_id = GP_BRUSH_ICON_BLOCK; + brush->gpencil_settings->brush_type = GP_BRUSH_TYPE_DRAW; + + brush->smooth_stroke_radius = SMOOTH_STROKE_RADIUS; + brush->smooth_stroke_factor = SMOOTH_STROKE_FACTOR; + + /* Marker brush */ + brush = BKE_brush_add_gpencil(bmain, ts, "Draw Marker"); + brush->size = 80.0f; + brush->gpencil_settings->flag |= (GP_BRUSH_USE_PRESSURE | GP_BRUSH_ENABLE_CURSOR); + brush->gpencil_settings->draw_sensitivity = 1.0f; + + brush->gpencil_settings->draw_strength = 1.0f; + + brush->gpencil_settings->flag |= GP_BRUSH_GROUP_RANDOM; + brush->gpencil_settings->draw_random_press = 0.374f; + brush->gpencil_settings->draw_random_strength = 0.0f; + + brush->gpencil_settings->draw_jitter = 0.0f; + brush->gpencil_settings->flag |= GP_BRUSH_USE_JITTER_PRESSURE; + + brush->gpencil_settings->draw_angle = M_PI_4; /* 45 degrees */ + brush->gpencil_settings->draw_angle_factor = 1.0f; + + brush->gpencil_settings->flag |= GP_BRUSH_GROUP_SETTINGS; + brush->gpencil_settings->draw_smoothfac = 0.1f; + brush->gpencil_settings->draw_smoothlvl = 1; + brush->gpencil_settings->thick_smoothfac = 1.0f; + brush->gpencil_settings->thick_smoothlvl = 3; + brush->gpencil_settings->draw_subdivide = 1; + brush->gpencil_settings->draw_random_sub = 0.0f; + brush->gpencil_settings->icon_id = GP_BRUSH_ICON_MARKER; + brush->gpencil_settings->brush_type = GP_BRUSH_TYPE_DRAW; + + brush->smooth_stroke_radius = SMOOTH_STROKE_RADIUS; + brush->smooth_stroke_factor = SMOOTH_STROKE_FACTOR; + + /* Fill brush */ + brush = BKE_brush_add_gpencil(bmain, ts, "Fill Area"); + brush->size = 1.0f; + brush->gpencil_settings->flag |= GP_BRUSH_ENABLE_CURSOR; + brush->gpencil_settings->draw_sensitivity = 1.0f; + brush->gpencil_settings->fill_leak = 3; + brush->gpencil_settings->fill_threshold = 0.1f; + brush->gpencil_settings->fill_simplylvl = 1; + brush->gpencil_settings->icon_id = GP_BRUSH_ICON_FILL; + brush->gpencil_settings->brush_type = GP_BRUSH_TYPE_FILL; + + brush->gpencil_settings->draw_smoothfac = 0.1f; + brush->gpencil_settings->draw_smoothlvl = 1; + brush->gpencil_settings->thick_smoothfac = 1.0f; + brush->gpencil_settings->thick_smoothlvl = 3; + brush->gpencil_settings->draw_subdivide = 1; + + brush->smooth_stroke_radius = SMOOTH_STROKE_RADIUS; + brush->smooth_stroke_factor = SMOOTH_STROKE_FACTOR; + + brush->gpencil_settings->draw_strength = 1.0f; + + /* Soft Eraser brush */ + brush = BKE_brush_add_gpencil(bmain, ts, "Eraser Soft"); + brush->size = 30.0f; + brush->gpencil_settings->flag |= (GP_BRUSH_ENABLE_CURSOR | GP_BRUSH_DEFAULT_ERASER); + brush->gpencil_settings->icon_id = GP_BRUSH_ICON_ERASE_SOFT; + brush->gpencil_settings->brush_type = GP_BRUSH_TYPE_ERASE; + brush->gpencil_settings->eraser_mode = GP_BRUSH_ERASER_SOFT; + + /* Hard Eraser brush */ + brush = BKE_brush_add_gpencil(bmain, ts, "Eraser Hard"); + brush->size = 30.0f; + brush->gpencil_settings->flag |= GP_BRUSH_ENABLE_CURSOR; + brush->gpencil_settings->icon_id = GP_BRUSH_ICON_ERASE_HARD; + brush->gpencil_settings->brush_type = GP_BRUSH_TYPE_ERASE; + brush->gpencil_settings->eraser_mode = GP_BRUSH_ERASER_HARD; + + /* Stroke Eraser brush */ + brush = BKE_brush_add_gpencil(bmain, ts, "Eraser Stroke"); + brush->size = 30.0f; + brush->gpencil_settings->flag |= GP_BRUSH_ENABLE_CURSOR; + brush->gpencil_settings->icon_id = GP_BRUSH_ICON_ERASE_STROKE; + brush->gpencil_settings->brush_type = GP_BRUSH_TYPE_ERASE; + brush->gpencil_settings->eraser_mode = GP_BRUSH_ERASER_STROKE; + + /* set defaut brush */ + BKE_paint_brush_set(paint, deft); + +} + +/* get the active gp-brush for editing */ +Brush *BKE_brush_getactive_gpencil(ToolSettings *ts) +{ + /* error checking */ + if (ELEM(NULL, ts, ts->gp_paint)) { + return NULL; + } + Paint *paint = &ts->gp_paint->paint; + + return paint->brush; +} + struct Brush *BKE_brush_first_search(struct Main *bmain, const eObjectMode ob_mode) { Brush *brush; @@ -197,6 +570,12 @@ void BKE_brush_copy_data(Main *UNUSED(bmain), Brush *brush_dst, const Brush *bru } brush_dst->curve = curvemapping_copy(brush_src->curve); + if (brush_src->gpencil_settings != NULL) { + brush_dst->gpencil_settings = MEM_dupallocN(brush_src->gpencil_settings); + brush_dst->gpencil_settings->curve_sensitivity = curvemapping_copy(brush_src->gpencil_settings->curve_sensitivity); + brush_dst->gpencil_settings->curve_strength = curvemapping_copy(brush_src->gpencil_settings->curve_strength); + brush_dst->gpencil_settings->curve_jitter = curvemapping_copy(brush_src->gpencil_settings->curve_jitter); + } /* enable fake user by default */ id_fake_user_set(&brush_dst->id); @@ -215,11 +594,18 @@ void BKE_brush_free(Brush *brush) if (brush->icon_imbuf) { IMB_freeImBuf(brush->icon_imbuf); } - curvemapping_free(brush->curve); + if (brush->gpencil_settings != NULL) { + curvemapping_free(brush->gpencil_settings->curve_sensitivity); + curvemapping_free(brush->gpencil_settings->curve_strength); + curvemapping_free(brush->gpencil_settings->curve_jitter); + MEM_SAFE_FREE(brush->gpencil_settings); + } + MEM_SAFE_FREE(brush->gradient); + BKE_previewimg_free(&(brush->preview)); } diff --git a/source/blender/blenkernel/intern/bvhutils.c b/source/blender/blenkernel/intern/bvhutils.c index 1a7c4e2a4a0..19ac81b4bb7 100644 --- a/source/blender/blenkernel/intern/bvhutils.c +++ b/source/blender/blenkernel/intern/bvhutils.c @@ -495,12 +495,39 @@ BVHTree *bvhtree_from_editmesh_verts_ex( BVHTree *bvhtree_from_editmesh_verts( BVHTreeFromEditMesh *data, BMEditMesh *em, - float epsilon, int tree_type, int axis) + float epsilon, int tree_type, int axis, BVHCache **bvh_cache) { - return bvhtree_from_editmesh_verts_ex( - data, em, - NULL, -1, - epsilon, tree_type, axis); + if (bvh_cache) { + BLI_rw_mutex_lock(&cache_rwlock, THREAD_LOCK_READ); + data->cached = bvhcache_find(*bvh_cache, BVHTREE_FROM_EM_VERTS, &data->tree); + BLI_rw_mutex_unlock(&cache_rwlock); + + if (data->cached == false) { + BLI_rw_mutex_lock(&cache_rwlock, THREAD_LOCK_WRITE); + data->cached = bvhcache_find( + *bvh_cache, BVHTREE_FROM_EM_VERTS, &data->tree); + if (data->cached == false) { + data->tree = bvhtree_from_editmesh_verts_ex( + data, em, + NULL, -1, + epsilon, tree_type, axis); + + /* Save on cache for later use */ + /* printf("BVHTree built and saved on cache\n"); */ + bvhcache_insert( + bvh_cache, data->tree, BVHTREE_FROM_EM_VERTS); + } + BLI_rw_mutex_unlock(&cache_rwlock); + } + } + else { + data->tree = bvhtree_from_editmesh_verts_ex( + data, em, + NULL, -1, + epsilon, tree_type, axis); + } + + return data->tree; } /** @@ -649,12 +676,39 @@ BVHTree *bvhtree_from_editmesh_edges_ex( BVHTree *bvhtree_from_editmesh_edges( BVHTreeFromEditMesh *data, BMEditMesh *em, - float epsilon, int tree_type, int axis) + float epsilon, int tree_type, int axis, BVHCache **bvh_cache) { - return bvhtree_from_editmesh_edges_ex( - data, em, - NULL, -1, - epsilon, tree_type, axis); + if (bvh_cache) { + BLI_rw_mutex_lock(&cache_rwlock, THREAD_LOCK_READ); + data->cached = bvhcache_find(*bvh_cache, BVHTREE_FROM_EM_EDGES, &data->tree); + BLI_rw_mutex_unlock(&cache_rwlock); + + if (data->cached == false) { + BLI_rw_mutex_lock(&cache_rwlock, THREAD_LOCK_WRITE); + data->cached = bvhcache_find( + *bvh_cache, BVHTREE_FROM_EM_EDGES, &data->tree); + if (data->cached == false) { + data->tree = bvhtree_from_editmesh_edges_ex( + data, em, + NULL, -1, + epsilon, tree_type, axis); + + /* Save on cache for later use */ + /* printf("BVHTree built and saved on cache\n"); */ + bvhcache_insert( + bvh_cache, data->tree, BVHTREE_FROM_EM_EDGES); + } + BLI_rw_mutex_unlock(&cache_rwlock); + } + } + else { + data->tree = bvhtree_from_editmesh_edges_ex( + data, em, + NULL, -1, + epsilon, tree_type, axis); + } + + return data->tree; } /** @@ -1407,6 +1461,11 @@ BVHTree *BKE_bvhtree_from_mesh_get( BLI_rw_mutex_unlock(&cache_rwlock); } break; + case BVHTREE_FROM_EM_VERTS: + case BVHTREE_FROM_EM_EDGES: + case BVHTREE_FROM_EM_LOOPTRI: + BLI_assert(false); + break; } if (data_cp.tree != NULL) { diff --git a/source/blender/blenkernel/intern/camera.c b/source/blender/blenkernel/intern/camera.c index 4203e0455f8..6e50b6e41fa 100644 --- a/source/blender/blenkernel/intern/camera.c +++ b/source/blender/blenkernel/intern/camera.c @@ -70,12 +70,15 @@ void BKE_camera_init(Camera *cam) cam->sensor_x = DEFAULT_SENSOR_WIDTH; cam->sensor_y = DEFAULT_SENSOR_HEIGHT; cam->clipsta = 0.1f; - cam->clipend = 100.0f; + cam->clipend = 1000.0f; cam->drawsize = 0.5f; cam->ortho_scale = 6.0; cam->flag |= CAM_SHOWPASSEPARTOUT; cam->passepartalpha = 0.5f; + cam->gpu_dof.fstop = 128.0f; + cam->gpu_dof.ratio = 1.0f; + /* stereoscopy 3d */ cam->stereo.interocular_distance = 0.065f; cam->stereo.convergence_distance = 30.f * 0.065f; @@ -102,19 +105,9 @@ void *BKE_camera_add(Main *bmain, const char *name) * * \param flag Copying options (see BKE_library.h's LIB_ID_COPY_... flags for more). */ -void BKE_camera_copy_data(Main *UNUSED(bmain), Camera *cam_dst, const Camera *cam_src, const int flag) +void BKE_camera_copy_data(Main *UNUSED(bmain), Camera *cam_dst, const Camera *cam_src, const int UNUSED(flag)) { BLI_duplicatelist(&cam_dst->bg_images, &cam_src->bg_images); - if ((flag & LIB_ID_CREATE_NO_USER_REFCOUNT) == 0) { - for (CameraBGImage *bgpic = cam_dst->bg_images.first; bgpic; bgpic = bgpic->next) { - if (bgpic->source == CAM_BGIMG_SOURCE_IMAGE) { - id_us_plus((ID *)bgpic->ima); - } - else if (bgpic->source == CAM_BGIMG_SOURCE_MOVIE) { - id_us_plus((ID *)bgpic->clip); - } - } - } } Camera *BKE_camera_copy(Main *bmain, const Camera *cam) @@ -132,14 +125,6 @@ void BKE_camera_make_local(Main *bmain, Camera *cam, const bool lib_local) /** Free (or release) any data used by this camera (does not free the camera itself). */ void BKE_camera_free(Camera *ca) { - for (CameraBGImage *bgpic = ca->bg_images.first; bgpic; bgpic = bgpic->next) { - if (bgpic->source == CAM_BGIMG_SOURCE_IMAGE) { - id_us_min((ID *)bgpic->ima); - } - else if (bgpic->source == CAM_BGIMG_SOURCE_MOVIE) { - id_us_min((ID *)bgpic->clip); - } - } BLI_freelistN(&ca->bg_images); BKE_animdata_free((ID *)ca, false); diff --git a/source/blender/blenkernel/intern/cdderivedmesh.c b/source/blender/blenkernel/intern/cdderivedmesh.c index 72a1f941c26..3f50321b4d5 100644 --- a/source/blender/blenkernel/intern/cdderivedmesh.c +++ b/source/blender/blenkernel/intern/cdderivedmesh.c @@ -655,8 +655,8 @@ DerivedMesh *CDDM_from_curve(Object *ob) { ListBase disp = {NULL, NULL}; - if (ob->curve_cache) { - disp = ob->curve_cache->disp; + if (ob->runtime.curve_cache) { + disp = ob->runtime.curve_cache->disp; } return CDDM_from_curve_displist(ob, &disp); diff --git a/source/blender/blenkernel/intern/cloth.c b/source/blender/blenkernel/intern/cloth.c index ccef747a31c..caf5b94b30e 100644 --- a/source/blender/blenkernel/intern/cloth.c +++ b/source/blender/blenkernel/intern/cloth.c @@ -924,7 +924,7 @@ static void cloth_from_mesh ( ClothModifierData *clmd, Mesh *mesh ) } /*************************************************************************************** - * SPRING NETWORK GWN_BATCH_BUILDING IMPLEMENTATION BEGIN + * SPRING NETWORK GPU_BATCH_BUILDING IMPLEMENTATION BEGIN ***************************************************************************************/ BLI_INLINE void spring_verts_ordered_set(ClothSpring *spring, int v0, int v1) @@ -1506,5 +1506,5 @@ static int cloth_build_springs ( ClothModifierData *clmd, Mesh *mesh ) } /* cloth_build_springs */ /*************************************************************************************** - * SPRING NETWORK GWN_BATCH_BUILDING IMPLEMENTATION END + * SPRING NETWORK GPU_BATCH_BUILDING IMPLEMENTATION END ***************************************************************************************/ diff --git a/source/blender/blenkernel/intern/collection.c b/source/blender/blenkernel/intern/collection.c index 0c93f304218..04e09d06405 100644 --- a/source/blender/blenkernel/intern/collection.c +++ b/source/blender/blenkernel/intern/collection.c @@ -57,7 +57,7 @@ /******************************** Prototypes ********************************/ -static bool collection_child_add(Collection *parent, Collection *collection, int flag, const bool add_us); +static bool collection_child_add(Collection *parent, Collection *collection, const int flag, const bool add_us); static bool collection_child_remove(Collection *parent, Collection *collection); static bool collection_object_add(Main *bmain, Collection *collection, Object *ob, int flag, const bool add_us); static bool collection_object_remove(Main *bmain, Collection *collection, Object *ob, const bool free_us); diff --git a/source/blender/blenkernel/intern/colortools.c b/source/blender/blenkernel/intern/colortools.c index ff4795afe87..d18572a57f6 100644 --- a/source/blender/blenkernel/intern/colortools.c +++ b/source/blender/blenkernel/intern/colortools.c @@ -282,6 +282,7 @@ void curvemap_reset(CurveMap *cuma, const rctf *clipr, int preset, int slope) case CURVE_PRESET_MID9: cuma->totpoint = 9; break; case CURVE_PRESET_ROUND: cuma->totpoint = 4; break; case CURVE_PRESET_ROOT: cuma->totpoint = 4; break; + case CURVE_PRESET_GAUSS: cuma->totpoint = 7; break; } cuma->curve = MEM_callocN(cuma->totpoint * sizeof(CurveMapPoint), "curve points"); @@ -352,6 +353,24 @@ void curvemap_reset(CurveMap *cuma, const rctf *clipr, int preset, int slope) cuma->curve[3].x = 1; cuma->curve[3].y = 0; break; + case CURVE_PRESET_GAUSS: + cuma->curve[0].x = 0; + cuma->curve[0].y = 0.025f; + cuma->curve[1].x = 0.16f; + cuma->curve[1].y = 0.135f; + cuma->curve[2].x = 0.298f; + cuma->curve[2].y = 0.36f; + + cuma->curve[3].x = 0.50f; + cuma->curve[3].y = 1.0f; + + cuma->curve[4].x = 0.70f; + cuma->curve[4].y = 0.36f; + cuma->curve[5].x = 0.84f; + cuma->curve[5].y = 0.135f; + cuma->curve[6].x = 1.0f; + cuma->curve[6].y = 0.025f; + break; } /* mirror curve in x direction to have positive slope diff --git a/source/blender/blenkernel/intern/constraint.c b/source/blender/blenkernel/intern/constraint.c index 4f772673e1d..ef412f0006e 100644 --- a/source/blender/blenkernel/intern/constraint.c +++ b/source/blender/blenkernel/intern/constraint.c @@ -496,7 +496,7 @@ static void contarget_get_lattice_mat(Object *ob, const char *substring, float m { Lattice *lt = (Lattice *)ob->data; - DispList *dl = ob->curve_cache ? BKE_displist_find(&ob->curve_cache->disp, DL_VERTS) : NULL; + DispList *dl = ob->runtime.curve_cache ? BKE_displist_find(&ob->runtime.curve_cache->disp, DL_VERTS) : NULL; const float *co = dl ? dl->verts : NULL; BPoint *bp = lt->def; @@ -1266,7 +1266,7 @@ static void followpath_get_tarmat(struct Depsgraph *UNUSED(depsgraph), * currently for paths to work it needs to go through the bevlist/displist system (ton) */ - if (ct->tar->curve_cache && ct->tar->curve_cache->path && ct->tar->curve_cache->path->data) { + if (ct->tar->runtime.curve_cache && ct->tar->runtime.curve_cache->path && ct->tar->runtime.curve_cache->path->data) { float quat[4]; if ((data->followflag & FOLLOWPATH_STATIC) == 0) { /* animated position along curve depending on time */ @@ -2037,7 +2037,7 @@ static void pycon_get_tarmat(struct Depsgraph *UNUSED(depsgraph), #endif if (VALID_CONS_TARGET(ct)) { - if (ct->tar->type == OB_CURVE && ct->tar->curve_cache == NULL) { + if (ct->tar->type == OB_CURVE && ct->tar->runtime.curve_cache == NULL) { unit_m4(ct->matrix); return; } @@ -3104,7 +3104,7 @@ static void clampto_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *tar BKE_object_minmax(ct->tar, curveMin, curveMax, true); /* get targetmatrix */ - if (data->tar->curve_cache && data->tar->curve_cache->path && data->tar->curve_cache->path->data) { + if (data->tar->runtime.curve_cache && data->tar->runtime.curve_cache->path && data->tar->runtime.curve_cache->path->data) { float vec[4], dir[3], totmat[4][4]; float curvetime; short clamp_axis; @@ -3641,7 +3641,7 @@ static void damptrack_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *t * - the min/max wrappers around (obvec . tarvec) result (stored temporarily in rangle) * are used to ensure that the smallest angle is chosen */ - cross_v3_v3v3(raxis, obvec, tarvec); + cross_v3_v3v3_hi_prec(raxis, obvec, tarvec); rangle = dot_v3v3(obvec, tarvec); rangle = acosf(max_ff(-1.0f, min_ff(1.0f, rangle))); @@ -3649,7 +3649,35 @@ static void damptrack_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *t /* construct rotation matrix from the axis-angle rotation found above * - this call takes care to make sure that the axis provided is a unit vector first */ - axis_angle_to_mat3(rmat, raxis, rangle); + float norm = normalize_v3(raxis); + + if (norm < FLT_EPSILON) { + /* if dot product is nonzero, while cross is zero, we have two opposite vectors! + * - this is an ambiguity in the math that needs to be resolved arbitrarily, + * or there will be a case where damped track strangely does nothing + * - to do that, rotate around a different local axis + */ + float tmpvec[3]; + + if (fabsf(rangle) < M_PI - 0.01f) { + return; + } + + rangle = M_PI; + copy_v3_v3(tmpvec, track_dir_vecs[(data->trackflag + 1) % 6]); + mul_mat3_m4_v3(cob->matrix, tmpvec); + cross_v3_v3v3(raxis, obvec, tmpvec); + + if (normalize_v3(raxis) == 0.0f) { + return; + } + } + else if (norm < 0.1f) { + /* near 0 and Pi arcsin has way better precision than arccos */ + rangle = (rangle > M_PI_2) ? M_PI - asinf(norm) : asinf(norm); + } + + axis_angle_normalized_to_mat3(rmat, raxis, rangle); /* rotate the owner in the way defined by this rotation matrix, then reapply the location since * we may have destroyed that in the process of multiplying the matrix diff --git a/source/blender/blenkernel/intern/context.c b/source/blender/blenkernel/intern/context.c index b71d4343ef5..61900f98c3a 100644 --- a/source/blender/blenkernel/intern/context.c +++ b/source/blender/blenkernel/intern/context.c @@ -876,9 +876,9 @@ void CTX_wm_menu_set(bContext *C, ARegion *menu) C->wm.menu = menu; } -void CTX_wm_gizmo_group_set(bContext *C, struct wmGizmoGroup *mgroup) +void CTX_wm_gizmo_group_set(bContext *C, struct wmGizmoGroup *gzgroup) { - C->wm.gizmo_group = mgroup; + C->wm.gizmo_group = gzgroup; } void CTX_wm_operator_poll_msg_set(bContext *C, const char *msg) @@ -1016,6 +1016,10 @@ int CTX_data_mode_enum_ex(const Object *obedit, const Object *ob, const eObjectM else if (object_mode & OB_MODE_VERTEX_PAINT) return CTX_MODE_PAINT_VERTEX; else if (object_mode & OB_MODE_TEXTURE_PAINT) return CTX_MODE_PAINT_TEXTURE; else if (object_mode & OB_MODE_PARTICLE_EDIT) return CTX_MODE_PARTICLE; + else if (object_mode & OB_MODE_GPENCIL_PAINT) return CTX_MODE_GPENCIL_PAINT; + else if (object_mode & OB_MODE_GPENCIL_EDIT) return CTX_MODE_GPENCIL_EDIT; + else if (object_mode & OB_MODE_GPENCIL_SCULPT) return CTX_MODE_GPENCIL_SCULPT; + else if (object_mode & OB_MODE_GPENCIL_WEIGHT) return CTX_MODE_GPENCIL_WEIGHT; } } @@ -1047,6 +1051,10 @@ static const char *data_mode_strings[] = { "imagepaint", "particlemode", "objectmode", + "greasepencil_paint", + "greasepencil_edit", + "greasepencil_sculpt", + "greasepencil_weight", NULL }; BLI_STATIC_ASSERT(ARRAY_SIZE(data_mode_strings) == CTX_MODE_NUM + 1, "Must have a string for each context mode") @@ -1215,17 +1223,7 @@ bGPDlayer *CTX_data_active_gpencil_layer(const bContext *C) return ctx_data_pointer_get(C, "active_gpencil_layer"); } -bGPDpalette *CTX_data_active_gpencil_palette(const bContext *C) -{ - return ctx_data_pointer_get(C, "active_gpencil_palette"); -} - -bGPDpalettecolor *CTX_data_active_gpencil_palettecolor(const bContext *C) -{ - return ctx_data_pointer_get(C, "active_gpencil_palettecolor"); -} - -bGPDbrush *CTX_data_active_gpencil_brush(const bContext *C) +Brush *CTX_data_active_gpencil_brush(const bContext *C) { return ctx_data_pointer_get(C, "active_gpencil_brush"); } diff --git a/source/blender/blenkernel/intern/curve.c b/source/blender/blenkernel/intern/curve.c index 33a24f77937..39b28540205 100644 --- a/source/blender/blenkernel/intern/curve.c +++ b/source/blender/blenkernel/intern/curve.c @@ -1752,11 +1752,11 @@ void BKE_curve_bevel_make( BKE_displist_make_curveTypes_forRender(depsgraph, scene, cu->bevobj, &bevdisp, NULL, false, use_render_resolution); dl = bevdisp.first; } - else if (cu->bevobj->curve_cache) { - dl = cu->bevobj->curve_cache->disp.first; + else if (cu->bevobj->runtime.curve_cache) { + dl = cu->bevobj->runtime.curve_cache->disp.first; } else { - BLI_assert(cu->bevobj->curve_cache != NULL); + BLI_assert(cu->bevobj->runtime.curve_cache != NULL); dl = NULL; } @@ -2669,14 +2669,14 @@ void BKE_curve_bevelList_make(Object *ob, ListBase *nurbs, bool for_render) ELEM(cu->bevfac2_mapping, CU_BEVFAC_MAP_SEGMENT, CU_BEVFAC_MAP_SPLINE); - bev = &ob->curve_cache->bev; + bev = &ob->runtime.curve_cache->bev; /* do we need to calculate the radius for each point? */ /* do_radius = (cu->bevobj || cu->taperobj || (cu->flag & CU_FRONT) || (cu->flag & CU_BACK)) ? 0 : 1; */ /* STEP 1: MAKE POLYS */ - BKE_curve_bevelList_free(&ob->curve_cache->bev); + BKE_curve_bevelList_free(&ob->runtime.curve_cache->bev); nu = nurbs->first; if (cu->editnurb && ob->type != OB_FONT) { is_editmode = 1; diff --git a/source/blender/blenkernel/intern/deform.c b/source/blender/blenkernel/intern/deform.c index d08e3643ca7..ddf9840a32e 100644 --- a/source/blender/blenkernel/intern/deform.c +++ b/source/blender/blenkernel/intern/deform.c @@ -74,7 +74,9 @@ bDeformGroup *BKE_defgroup_new(Object *ob, const char *name) BLI_addtail(&ob->defbase, defgroup); defgroup_unique_name(defgroup, ob); - BKE_mesh_batch_cache_dirty(ob->data, BKE_MESH_BATCH_DIRTY_ALL); + if (ob->type != OB_GPENCIL) { + BKE_mesh_batch_cache_dirty(ob->data, BKE_MESH_BATCH_DIRTY_ALL); + } return defgroup; } diff --git a/source/blender/blenkernel/intern/displist.c b/source/blender/blenkernel/intern/displist.c index 34fd32b2908..562e2257ea0 100644 --- a/source/blender/blenkernel/intern/displist.c +++ b/source/blender/blenkernel/intern/displist.c @@ -692,10 +692,10 @@ static float displist_calc_taper(Depsgraph *depsgraph, Scene *scene, Object *tap if (taperobj == NULL || taperobj->type != OB_CURVE) return 1.0; - dl = taperobj->curve_cache ? taperobj->curve_cache->disp.first : NULL; + dl = taperobj->runtime.curve_cache ? taperobj->runtime.curve_cache->disp.first : NULL; if (dl == NULL) { BKE_displist_make_curveTypes(depsgraph, scene, taperobj, 0); - dl = taperobj->curve_cache->disp.first; + dl = taperobj->runtime.curve_cache->disp.first; } if (dl) { float minx, dx, *fp; @@ -738,17 +738,17 @@ void BKE_displist_make_mball(Depsgraph *depsgraph, Scene *scene, Object *ob) return; if (ob == BKE_mball_basis_find(scene, ob)) { - if (ob->curve_cache) { - BKE_displist_free(&(ob->curve_cache->disp)); + if (ob->runtime.curve_cache) { + BKE_displist_free(&(ob->runtime.curve_cache->disp)); } else { - ob->curve_cache = MEM_callocN(sizeof(CurveCache), "CurveCache for MBall"); + ob->runtime.curve_cache = MEM_callocN(sizeof(CurveCache), "CurveCache for MBall"); } - BKE_mball_polygonize(depsgraph, scene, ob, &ob->curve_cache->disp); + BKE_mball_polygonize(depsgraph, scene, ob, &ob->runtime.curve_cache->disp); BKE_mball_texspace_calc(ob); - object_deform_mball(ob, &ob->curve_cache->disp); + object_deform_mball(ob, &ob->runtime.curve_cache->disp); /* NOP for MBALLs anyway... */ boundbox_displist_object(ob); @@ -1314,7 +1314,7 @@ void BKE_displist_make_surf( } if (!for_orco) { - BKE_nurbList_duplicate(&ob->curve_cache->deformed_nurbs, &nubase); + BKE_nurbList_duplicate(&ob->runtime.curve_cache->deformed_nurbs, &nubase); curve_calc_modifiers_post(depsgraph, scene, ob, &nubase, dispbase, r_dm_final, for_render, use_render_resolution); } @@ -1558,15 +1558,15 @@ static void do_makeDispListCurveTypes( ListBase dlbev; ListBase nubase = {NULL, NULL}; - BKE_curve_bevelList_free(&ob->curve_cache->bev); + BKE_curve_bevelList_free(&ob->runtime.curve_cache->bev); /* We only re-evaluate path if evaluation is not happening for orco. * If the calculation happens for orco, we should never free data which * was needed before and only not needed for orco calculation. */ if (!for_orco) { - if (ob->curve_cache->path) free_path(ob->curve_cache->path); - ob->curve_cache->path = NULL; + if (ob->runtime.curve_cache->path) free_path(ob->runtime.curve_cache->path); + ob->runtime.curve_cache->path = NULL; } if (ob->type == OB_FONT) { @@ -1590,7 +1590,7 @@ static void do_makeDispListCurveTypes( } else { float widfac = cu->width - 1.0f; - BevList *bl = ob->curve_cache->bev.first; + BevList *bl = ob->runtime.curve_cache->bev.first; Nurb *nu = nubase.first; for (; bl && nu; bl = bl->next, nu = nu->next) { @@ -1777,7 +1777,7 @@ static void do_makeDispListCurveTypes( } if (!for_orco) { - BKE_nurbList_duplicate(&ob->curve_cache->deformed_nurbs, &nubase); + BKE_nurbList_duplicate(&ob->runtime.curve_cache->deformed_nurbs, &nubase); curve_calc_modifiers_post(depsgraph, scene, ob, &nubase, dispbase, r_dm_final, for_render, use_render_resolution); } @@ -1801,11 +1801,11 @@ void BKE_displist_make_curveTypes(Depsgraph *depsgraph, Scene *scene, Object *ob BKE_object_free_derived_caches(ob); - if (!ob->curve_cache) { - ob->curve_cache = MEM_callocN(sizeof(CurveCache), "CurveCache for curve types"); + if (!ob->runtime.curve_cache) { + ob->runtime.curve_cache = MEM_callocN(sizeof(CurveCache), "CurveCache for curve types"); } - dispbase = &(ob->curve_cache->disp); + dispbase = &(ob->runtime.curve_cache->disp); do_makeDispListCurveTypes(depsgraph, scene, ob, dispbase, &ob->derivedFinal, 0, for_orco, 0); @@ -1817,8 +1817,8 @@ void BKE_displist_make_curveTypes_forRender( DerivedMesh **r_dm_final, const bool for_orco, const bool use_render_resolution) { - if (ob->curve_cache == NULL) { - ob->curve_cache = MEM_callocN(sizeof(CurveCache), "CurveCache for Curve"); + if (ob->runtime.curve_cache == NULL) { + ob->runtime.curve_cache = MEM_callocN(sizeof(CurveCache), "CurveCache for Curve"); } do_makeDispListCurveTypes(depsgraph, scene, ob, dispbase, r_dm_final, true, for_orco, use_render_resolution); @@ -1827,8 +1827,8 @@ void BKE_displist_make_curveTypes_forRender( void BKE_displist_make_curveTypes_forOrco( Depsgraph *depsgraph, Scene *scene, Object *ob, ListBase *dispbase) { - if (ob->curve_cache == NULL) { - ob->curve_cache = MEM_callocN(sizeof(CurveCache), "CurveCache for Curve"); + if (ob->runtime.curve_cache == NULL) { + ob->runtime.curve_cache = MEM_callocN(sizeof(CurveCache), "CurveCache for Curve"); } do_makeDispListCurveTypes(depsgraph, scene, ob, dispbase, NULL, 1, 1, 1); @@ -1903,7 +1903,7 @@ static void boundbox_displist_object(Object *ob) float min[3], max[3]; INIT_MINMAX(min, max); - BKE_displist_minmax(&ob->curve_cache->disp, min, max); + BKE_displist_minmax(&ob->runtime.curve_cache->disp, min, max); BKE_boundbox_init_from_minmax(ob->bb, min, max); ob->bb->flag &= ~BOUNDBOX_DIRTY; diff --git a/source/blender/blenkernel/intern/editderivedmesh.c b/source/blender/blenkernel/intern/editderivedmesh.c index 4c2f513007a..87d93db640d 100644 --- a/source/blender/blenkernel/intern/editderivedmesh.c +++ b/source/blender/blenkernel/intern/editderivedmesh.c @@ -203,7 +203,7 @@ static void emDM_calcLoopNormalsSpaceArray( cd_loop_clnors_offset = clnors_data ? -1 : CustomData_get_offset(&bm->ldata, CD_CUSTOMLOOPNORMAL); BM_loops_calc_normal_vcos(bm, vertexCos, vertexNos, polyNos, use_split_normals, split_angle, loopNos, - r_lnors_spacearr, clnors_data, cd_loop_clnors_offset); + r_lnors_spacearr, clnors_data, cd_loop_clnors_offset, false); #ifdef DEBUG_CLNORS if (r_lnors_spacearr) { int i; diff --git a/source/blender/blenkernel/intern/editmesh.c b/source/blender/blenkernel/intern/editmesh.c index b63ab276b14..7d66d25c58a 100644 --- a/source/blender/blenkernel/intern/editmesh.c +++ b/source/blender/blenkernel/intern/editmesh.c @@ -246,3 +246,26 @@ float (*BKE_editmesh_vertexCos_get_orco(BMEditMesh *em, int *r_numVerts))[3] return orco; } + +void BKE_editmesh_lnorspace_update(BMEditMesh *em) +{ + BMesh *bm = em->bm; + + /* We need to create clnors data if none exist yet, otherwise there is no way to edit them. + * Similar code to MESH_OT_customdata_custom_splitnormals_add operator, we want to keep same shading + * in case we were using autosmooth so far... + * Note: there is a problem here, which is that if someone starts a normal editing operation on previously + * autosmooth-ed mesh, and cancel that operation, generated clnors data remain, with related sharp edges + * (and hence autosmooth is 'lost'). + * Not sure how critical this is, and how to fix that issue? */ + if (!CustomData_has_layer(&bm->ldata, CD_CUSTOMLOOPNORMAL)) { + Mesh *me = em->ob->data; + if (me->flag & ME_AUTOSMOOTH) { + BM_edges_sharp_from_angle_set(bm, me->smoothresh); + + me->drawflag |= ME_DRAWSHARP; + } + } + + BM_lnorspace_update(bm); +} diff --git a/source/blender/blenkernel/intern/effect.c b/source/blender/blenkernel/intern/effect.c index b6eb26443ea..52ea4bf4a5e 100644 --- a/source/blender/blenkernel/intern/effect.c +++ b/source/blender/blenkernel/intern/effect.c @@ -163,10 +163,10 @@ static void precalculate_effector(struct Depsgraph *depsgraph, EffectorCache *ef if (eff->pd->forcefield == PFIELD_GUIDE && eff->ob->type==OB_CURVE) { Curve *cu= eff->ob->data; if (cu->flag & CU_PATH) { - if (eff->ob->curve_cache == NULL || eff->ob->curve_cache->path==NULL || eff->ob->curve_cache->path->data==NULL) + if (eff->ob->runtime.curve_cache == NULL || eff->ob->runtime.curve_cache->path==NULL || eff->ob->runtime.curve_cache->path->data==NULL) BKE_displist_make_curveTypes(depsgraph, eff->scene, eff->ob, 0); - if (eff->ob->curve_cache->path && eff->ob->curve_cache->path->data) { + if (eff->ob->runtime.curve_cache->path && eff->ob->runtime.curve_cache->path->data) { where_on_path(eff->ob, 0.0, eff->guide_loc, eff->guide_dir, NULL, &eff->guide_radius, NULL); mul_m4_v3(eff->ob->obmat, eff->guide_loc); mul_mat3_m4_v3(eff->ob->obmat, eff->guide_dir); diff --git a/source/blender/blenkernel/intern/font.c b/source/blender/blenkernel/intern/font.c index 36633663f9d..b5fba6d30e8 100644 --- a/source/blender/blenkernel/intern/font.c +++ b/source/blender/blenkernel/intern/font.c @@ -1075,8 +1075,8 @@ makebreak: /* TEXT ON CURVE */ /* Note: Only OB_CURVE objects could have a path */ if (cu->textoncurve && cu->textoncurve->type == OB_CURVE) { - BLI_assert(cu->textoncurve->curve_cache != NULL); - if (cu->textoncurve->curve_cache->path) { + BLI_assert(cu->textoncurve->runtime.curve_cache != NULL); + if (cu->textoncurve->runtime.curve_cache->path) { float distfac, imat[4][4], imat3[3][3], cmat[3][3]; float minx, maxx, miny, maxy; float timeofs, sizefac; @@ -1106,7 +1106,7 @@ makebreak: /* we put the x-coordinaat exact at the curve, the y is rotated */ /* length correction */ - distfac = sizefac * cu->textoncurve->curve_cache->path->totdist / (maxx - minx); + distfac = sizefac * cu->textoncurve->runtime.curve_cache->path->totdist / (maxx - minx); timeofs = 0.0f; if (distfac > 1.0f) { diff --git a/source/blender/blenkernel/intern/gpencil.c b/source/blender/blenkernel/intern/gpencil.c index e89508fd6c0..fdb1123e1c7 100644 --- a/source/blender/blenkernel/intern/gpencil.c +++ b/source/blender/blenkernel/intern/gpencil.c @@ -39,26 +39,83 @@ #include "BLI_blenlib.h" #include "BLI_utildefines.h" #include "BLI_math_vector.h" +#include "BLI_math_color.h" #include "BLI_string_utils.h" +#include "BLI_rand.h" +#include "BLI_ghash.h" #include "BLT_translation.h" +#include "DNA_anim_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_material_types.h" #include "DNA_gpencil_types.h" #include "DNA_userdef_types.h" #include "DNA_scene_types.h" +#include "DNA_object_types.h" +#include "BKE_context.h" +#include "BKE_action.h" #include "BKE_animsys.h" #include "BKE_global.h" #include "BKE_gpencil.h" #include "BKE_colortools.h" +#include "BKE_icons.h" #include "BKE_library.h" #include "BKE_main.h" +#include "BKE_object.h" +#include "BKE_material.h" +#include "DEG_depsgraph.h" /* ************************************************** */ -/* GENERAL STUFF */ +/* Draw Engine */ -/* --------- Memory Management ------------ */ +void(*BKE_gpencil_batch_cache_dirty_cb)(bGPdata *gpd) = NULL; +void(*BKE_gpencil_batch_cache_free_cb)(bGPdata *gpd) = NULL; + +void BKE_gpencil_batch_cache_dirty(bGPdata *gpd) +{ + if (gpd) { + DEG_id_tag_update(&gpd->id, OB_RECALC_DATA); + BKE_gpencil_batch_cache_dirty_cb(gpd); + } +} + +void BKE_gpencil_batch_cache_free(bGPdata *gpd) +{ + if (gpd) { + BKE_gpencil_batch_cache_free_cb(gpd); + } +} + +/* ************************************************** */ +/* Memory Management */ + +/* clean vertex groups weights */ +void BKE_gpencil_free_point_weights(MDeformVert *dvert) +{ + if (dvert == NULL) { + return; + } + MEM_SAFE_FREE(dvert->dw); +} + +void BKE_gpencil_free_stroke_weights(bGPDstroke *gps) +{ + if (gps == NULL) { + return; + } + + if (gps->dvert == NULL) { + return; + } + + for (int i = 0; i < gps->totpoints; i++) { + MDeformVert *dvert = &gps->dvert[i]; + BKE_gpencil_free_point_weights(dvert); + } +} /* free stroke, doesn't unlink from any listbase */ void BKE_gpencil_free_stroke(bGPDstroke *gps) @@ -66,10 +123,14 @@ void BKE_gpencil_free_stroke(bGPDstroke *gps) if (gps == NULL) { return; } - /* free stroke memory arrays, then stroke itself */ - if (gps->points) + if (gps->points) { MEM_freeN(gps->points); + } + if (gps->dvert) { + BKE_gpencil_free_stroke_weights(gps); + MEM_freeN(gps->dvert); + } if (gps->triangles) MEM_freeN(gps->triangles); @@ -92,6 +153,26 @@ bool BKE_gpencil_free_strokes(bGPDframe *gpf) return changed; } +/* Free strokes and colors belonging to a gp-frame */ +bool BKE_gpencil_free_frame_runtime_data(bGPDframe *derived_gpf) +{ + bGPDstroke *gps_next; + if (!derived_gpf) { + return false; + } + + /* free strokes */ + for (bGPDstroke *gps = derived_gpf->strokes.first; gps; gps = gps_next) { + gps_next = gps->next; + BKE_gpencil_free_stroke(gps); + } + BLI_listbase_clear(&derived_gpf->strokes); + + MEM_SAFE_FREE(derived_gpf); + + return true; +} + /* Free all of a gp-layer's frames */ void BKE_gpencil_free_frames(bGPDlayer *gpl) { @@ -111,101 +192,101 @@ void BKE_gpencil_free_frames(bGPDlayer *gpl) gpl->actframe = NULL; } -/* Free all of a gp-colors */ -static void free_gpencil_colors(bGPDpalette *palette) -{ - /* error checking */ - if (palette == NULL) { - return; - } - /* free colors */ - BLI_freelistN(&palette->colors); -} -/* Free all of the gp-palettes and colors */ -void BKE_gpencil_free_palettes(ListBase *list) +/* Free all of the gp-layers for a viewport (list should be &gpd->layers or so) */ +void BKE_gpencil_free_layers(ListBase *list) { - bGPDpalette *palette_next; + bGPDlayer *gpl_next; /* error checking */ - if (list == NULL) { - return; - } + if (list == NULL) return; - /* delete palettes */ - for (bGPDpalette *palette = list->first; palette; palette = palette_next) { - palette_next = palette->next; - /* free palette colors */ - free_gpencil_colors(palette); + /* delete layers */ + for (bGPDlayer *gpl = list->first; gpl; gpl = gpl_next) { + gpl_next = gpl->next; - MEM_freeN(palette); + /* free layers and their data */ + BKE_gpencil_free_frames(gpl); + BLI_freelinkN(list, gpl); } - BLI_listbase_clear(list); } -/* Free all of the gp-brushes for a viewport (list should be &gpd->brushes or so) */ -void BKE_gpencil_free_brushes(ListBase *list) +/* clear all runtime derived data */ +static void BKE_gpencil_clear_derived(bGPDlayer *gpl) { - bGPDbrush *brush_next; + GHashIterator gh_iter; - /* error checking */ - if (list == NULL) { + if (gpl->runtime.derived_data == NULL) { return; } - /* delete brushes */ - for (bGPDbrush *brush = list->first; brush; brush = brush_next) { - brush_next = brush->next; - /* free curves */ - if (brush->cur_sensitivity) { - curvemapping_free(brush->cur_sensitivity); - } - if (brush->cur_strength) { - curvemapping_free(brush->cur_strength); - } - if (brush->cur_jitter) { - curvemapping_free(brush->cur_jitter); + GHASH_ITER(gh_iter, gpl->runtime.derived_data) { + bGPDframe *gpf = (bGPDframe *)BLI_ghashIterator_getValue(&gh_iter); + if (gpf) { + BKE_gpencil_free_frame_runtime_data(gpf); } - - MEM_freeN(brush); } - BLI_listbase_clear(list); } -/* Free all of the gp-layers for a viewport (list should be &gpd->layers or so) */ -void BKE_gpencil_free_layers(ListBase *list) +/* Free all of the gp-layers temp data*/ +static void BKE_gpencil_free_layers_temp_data(ListBase *list) { bGPDlayer *gpl_next; /* error checking */ if (list == NULL) return; - /* delete layers */ for (bGPDlayer *gpl = list->first; gpl; gpl = gpl_next) { gpl_next = gpl->next; + BKE_gpencil_clear_derived(gpl); - /* free layers and their data */ - BKE_gpencil_free_frames(gpl); - BLI_freelinkN(list, gpl); + if (gpl->runtime.derived_data) { + BLI_ghash_free(gpl->runtime.derived_data, NULL, NULL); + gpl->runtime.derived_data = NULL; + } + } +} + +/* Free temp gpf derived frames */ +void BKE_gpencil_free_derived_frames(bGPdata *gpd) +{ + /* error checking */ + if (gpd == NULL) return; + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + BKE_gpencil_clear_derived(gpl); + + if (gpl->runtime.derived_data) { + BLI_ghash_free(gpl->runtime.derived_data, NULL, NULL); + gpl->runtime.derived_data = NULL; + } } } /** Free (or release) any data used by this grease pencil (does not free the gpencil itself). */ -void BKE_gpencil_free(bGPdata *gpd, bool free_palettes) +void BKE_gpencil_free(bGPdata *gpd, bool free_all) { + /* clear animation data */ BKE_animdata_free(&gpd->id, false); /* free layers */ + if (free_all) { + BKE_gpencil_free_layers_temp_data(&gpd->layers); + } BKE_gpencil_free_layers(&gpd->layers); - /* free palettes */ - if (free_palettes) { - BKE_gpencil_free_palettes(&gpd->palettes); + /* materials */ + MEM_SAFE_FREE(gpd->mat); + + /* free all data */ + if (free_all) { + /* clear cache */ + BKE_gpencil_batch_cache_free(gpd); } } -/* -------- Container Creation ---------- */ +/* ************************************************** */ +/* Container Creation */ /* add a new gp-frame to the given layer */ bGPDframe *BKE_gpencil_frame_addnew(bGPDlayer *gpl, int cframe) @@ -329,28 +410,31 @@ bGPDlayer *BKE_gpencil_layer_addnew(bGPdata *gpd, const char *name, bool setacti /* add to datablock */ BLI_addtail(&gpd->layers, gpl); - /* set basic settings */ - copy_v4_v4(gpl->color, U.gpencil_new_layer_col); - /* Since GPv2 thickness must be 0 */ - gpl->thickness = 0; - - gpl->opacity = 1.0f; - - /* onion-skinning settings */ - if (gpd->flag & GP_DATA_SHOW_ONIONSKINS) - gpl->flag |= GP_LAYER_ONIONSKIN; + /* annotation vs GP Object behaviour is slightly different */ + if (gpd->flag & GP_DATA_ANNOTATIONS) { + /* set default color of new strokes for this layer */ + copy_v4_v4(gpl->color, U.gpencil_new_layer_col); + gpl->opacity = 1.0f; - gpl->flag |= (GP_LAYER_GHOST_PREVCOL | GP_LAYER_GHOST_NEXTCOL); + /* set default thickness of new strokes for this layer */ + gpl->thickness = 3; - ARRAY_SET_ITEMS(gpl->gcolor_prev, 0.145098f, 0.419608f, 0.137255f); /* green */ - ARRAY_SET_ITEMS(gpl->gcolor_next, 0.125490f, 0.082353f, 0.529412f); /* blue */ - - /* high quality fill by default */ - gpl->flag |= GP_LAYER_HQ_FILL; + } + else { + /* thickness parameter represents "thickness change", not absolute thickness */ + gpl->thickness = 0; + gpl->opacity = 1.0f; + /* onion-skinning settings */ + gpl->onion_flag |= GP_LAYER_ONIONSKIN; + } /* auto-name */ BLI_strncpy(gpl->info, name, sizeof(gpl->info)); - BLI_uniquename(&gpd->layers, gpl, DATA_("GP_Layer"), '.', offsetof(bGPDlayer, info), sizeof(gpl->info)); + BLI_uniquename(&gpd->layers, gpl, + (gpd->flag & GP_DATA_ANNOTATIONS) ? DATA_("Note") : DATA_("GP_Layer"), + '.', + offsetof(bGPDlayer, info), + sizeof(gpl->info)); /* make this one the active one */ if (setactive) @@ -360,292 +444,153 @@ bGPDlayer *BKE_gpencil_layer_addnew(bGPdata *gpd, const char *name, bool setacti return gpl; } -/* add a new gp-palette and make it the active */ -bGPDpalette *BKE_gpencil_palette_addnew(bGPdata *gpd, const char *name, bool setactive) -{ - bGPDpalette *palette; - - /* check that list is ok */ - if (gpd == NULL) { - return NULL; - } - - /* allocate memory and add to end of list */ - palette = MEM_callocN(sizeof(bGPDpalette), "bGPDpalette"); - - /* add to datablock */ - BLI_addtail(&gpd->palettes, palette); - - /* set basic settings */ - /* auto-name */ - BLI_strncpy(palette->info, name, sizeof(palette->info)); - BLI_uniquename(&gpd->palettes, palette, DATA_("GP_Palette"), '.', offsetof(bGPDpalette, info), - sizeof(palette->info)); - - /* make this one the active one */ - /* NOTE: Always make this active if there's nothing else yet (T50123) */ - if ((setactive) || (gpd->palettes.first == gpd->palettes.last)) { - BKE_gpencil_palette_setactive(gpd, palette); - } - - /* return palette */ - return palette; -} - -/* create a set of default drawing brushes with predefined presets */ -void BKE_gpencil_brush_init_presets(ToolSettings *ts) +/* add a new gp-datablock */ +bGPdata *BKE_gpencil_data_addnew(Main *bmain, const char name[]) { - bGPDbrush *brush; - /* Basic brush */ - brush = BKE_gpencil_brush_addnew(ts, "Basic", true); - brush->thickness = 3.0f; - brush->flag &= ~GP_BRUSH_USE_RANDOM_PRESSURE; - brush->draw_sensitivity = 1.0f; - brush->flag |= GP_BRUSH_USE_PRESSURE; - - brush->flag &= ~GP_BRUSH_USE_RANDOM_STRENGTH; - brush->draw_strength = 1.0f; - brush->flag |= ~GP_BRUSH_USE_STENGTH_PRESSURE; - - brush->draw_random_press = 0.0f; - - brush->draw_jitter = 0.0f; - brush->flag |= GP_BRUSH_USE_JITTER_PRESSURE; - - brush->draw_angle = 0.0f; - brush->draw_angle_factor = 0.0f; - - brush->draw_smoothfac = 0.0f; - brush->draw_smoothlvl = 1; - brush->sublevel = 0; - brush->draw_random_sub = 0.0f; - - /* Pencil brush */ - brush = BKE_gpencil_brush_addnew(ts, "Pencil", false); - brush->thickness = 7.0f; - brush->flag &= ~GP_BRUSH_USE_RANDOM_PRESSURE; - brush->draw_sensitivity = 1.0f; - brush->flag |= GP_BRUSH_USE_PRESSURE; - - brush->flag &= ~GP_BRUSH_USE_RANDOM_STRENGTH; - brush->draw_strength = 0.7f; - brush->flag |= GP_BRUSH_USE_STENGTH_PRESSURE; - - brush->draw_random_press = 0.0f; - - brush->draw_jitter = 0.0f; - brush->flag |= GP_BRUSH_USE_JITTER_PRESSURE; - - brush->draw_angle = 0.0f; - brush->draw_angle_factor = 0.0f; - - brush->draw_smoothfac = 1.0f; - brush->draw_smoothlvl = 2; - brush->sublevel = 2; - brush->draw_random_sub = 0.0f; - - /* Ink brush */ - brush = BKE_gpencil_brush_addnew(ts, "Ink", false); - brush->thickness = 7.0f; - brush->flag &= ~GP_BRUSH_USE_RANDOM_PRESSURE; - brush->draw_sensitivity = 1.6f; - brush->flag |= GP_BRUSH_USE_PRESSURE; - - brush->flag &= ~GP_BRUSH_USE_RANDOM_STRENGTH; - brush->draw_strength = 1.0f; - brush->flag &= ~GP_BRUSH_USE_STENGTH_PRESSURE; - - brush->draw_random_press = 0.0f; - - brush->draw_jitter = 0.0f; - brush->flag |= GP_BRUSH_USE_JITTER_PRESSURE; - - brush->draw_angle = 0.0f; - brush->draw_angle_factor = 0.0f; + bGPdata *gpd; - brush->draw_smoothfac = 1.1f; - brush->draw_smoothlvl = 2; - brush->sublevel = 2; - brush->draw_random_sub = 0.0f; + /* allocate memory for a new block */ + gpd = BKE_libblock_alloc(bmain, ID_GD, name, 0); - /* Ink Noise brush */ - brush = BKE_gpencil_brush_addnew(ts, "Ink noise", false); - brush->thickness = 6.0f; - brush->flag |= GP_BRUSH_USE_RANDOM_PRESSURE; - brush->draw_sensitivity = 1.611f; - brush->flag |= GP_BRUSH_USE_PRESSURE; + /* initial settings */ + gpd->flag = (GP_DATA_DISPINFO | GP_DATA_EXPAND); - brush->flag &= ~GP_BRUSH_USE_RANDOM_STRENGTH; - brush->draw_strength = 1.0f; - brush->flag |= GP_BRUSH_USE_STENGTH_PRESSURE; + /* general flags */ + gpd->flag |= GP_DATA_VIEWALIGN; - brush->draw_random_press = 1.0f; + /* GP object specific settings */ + ARRAY_SET_ITEMS(gpd->line_color, 0.6f, 0.6f, 0.6f, 0.5f); - brush->draw_jitter = 0.0f; - brush->flag |= GP_BRUSH_USE_JITTER_PRESSURE; + gpd->xray_mode = GP_XRAY_3DSPACE; + gpd->runtime.batch_cache_data = NULL; + gpd->pixfactor = GP_DEFAULT_PIX_FACTOR; - brush->draw_angle = 0.0f; - brush->draw_angle_factor = 0.0f; + /* onion-skinning settings (datablock level) */ + gpd->onion_flag |= (GP_ONION_GHOST_PREVCOL | GP_ONION_GHOST_NEXTCOL); + gpd->onion_flag |= GP_ONION_FADE; + gpd->onion_mode = GP_ONION_MODE_RELATIVE; + gpd->onion_factor = 0.5f; + ARRAY_SET_ITEMS(gpd->gcolor_prev, 0.145098f, 0.419608f, 0.137255f); /* green */ + ARRAY_SET_ITEMS(gpd->gcolor_next, 0.125490f, 0.082353f, 0.529412f); /* blue */ + gpd->gstep = 1; + gpd->gstep_next = 1; - brush->draw_smoothfac = 1.1f; - brush->draw_smoothlvl = 2; - brush->sublevel = 2; - brush->draw_random_sub = 0.0f; + return gpd; +} - /* Marker brush */ - brush = BKE_gpencil_brush_addnew(ts, "Marker", false); - brush->thickness = 10.0f; - brush->flag &= ~GP_BRUSH_USE_RANDOM_PRESSURE; - brush->draw_sensitivity = 2.0f; - brush->flag &= ~GP_BRUSH_USE_PRESSURE; - brush->flag &= ~GP_BRUSH_USE_RANDOM_STRENGTH; - brush->draw_strength = 1.0f; - brush->flag &= ~GP_BRUSH_USE_STENGTH_PRESSURE; +/* ************************************************** */ +/* Primitive Creation */ +/* Utilities for easier bulk-creation of geometry */ - brush->draw_random_press = 0.0f; +/** + * Populate stroke with point data from data buffers + * + * \param array Flat array of point data values. Each entry has GP_PRIM_DATABUF_SIZE values + * \param mat 4x4 transform matrix to transform points into the right coordinate space + */ +void BKE_gpencil_stroke_add_points(bGPDstroke *gps, const float *array, const int totpoints, const float mat[4][4]) +{ + for (int i = 0; i < totpoints; i++) { + bGPDspoint *pt = &gps->points[i]; + const int x = GP_PRIM_DATABUF_SIZE * i; - brush->draw_jitter = 0.0f; - brush->flag |= GP_BRUSH_USE_JITTER_PRESSURE; + pt->x = array[x]; + pt->y = array[x + 1]; + pt->z = array[x + 2]; + mul_m4_v3(mat, &pt->x); - brush->draw_angle = M_PI_4; /* 45 degrees */ - brush->draw_angle_factor = 1.0f; + pt->pressure = array[x + 3]; + pt->strength = array[x + 4]; + } +} - brush->draw_smoothfac = 1.0f; - brush->draw_smoothlvl = 2; - brush->sublevel = 2; - brush->draw_random_sub = 0.0f; +/* Create a new stroke, with pre-allocated data buffers */ +bGPDstroke *BKE_gpencil_add_stroke(bGPDframe *gpf, int mat_idx, int totpoints, short thickness) +{ + /* allocate memory for a new stroke */ + bGPDstroke *gps = MEM_callocN(sizeof(bGPDstroke), "gp_stroke"); - /* Crayon brush */ - brush = BKE_gpencil_brush_addnew(ts, "Crayon", false); - brush->thickness = 10.0f; - brush->flag &= ~GP_BRUSH_USE_RANDOM_PRESSURE; - brush->draw_sensitivity = 3.0f; - brush->flag &= ~GP_BRUSH_USE_PRESSURE; + gps->thickness = thickness * 25; + gps->inittime = 0; - brush->flag &= ~GP_BRUSH_USE_RANDOM_STRENGTH; - brush->draw_strength = 0.140f; - brush->flag |= GP_BRUSH_USE_STENGTH_PRESSURE; + /* enable recalculation flag by default */ + gps->flag = GP_STROKE_RECALC_CACHES | GP_STROKE_3DSPACE; - brush->draw_random_press = 0.0f; + gps->totpoints = totpoints; + gps->points = MEM_callocN(sizeof(bGPDspoint) * gps->totpoints, "gp_stroke_points"); + gps->dvert = MEM_callocN(sizeof(MDeformVert) * gps->totpoints, "gp_stroke_weights"); - brush->draw_jitter = 0.0f; - brush->flag |= GP_BRUSH_USE_JITTER_PRESSURE; + /* initialize triangle memory to dummy data */ + gps->triangles = MEM_callocN(sizeof(bGPDtriangle), "GP Stroke triangulation"); + gps->flag |= GP_STROKE_RECALC_CACHES; + gps->tot_triangles = 0; - brush->draw_angle = 0.0f; - brush->draw_angle_factor = 0.0f; + gps->mat_nr = mat_idx; - brush->draw_smoothfac = 0.0f; - brush->draw_smoothlvl = 1; - brush->sublevel = 2; - brush->draw_random_sub = 0.5f; + /* add to frame */ + BLI_addtail(&gpf->strokes, gps); + return gps; } -/* add a new gp-brush and make it the active */ -bGPDbrush *BKE_gpencil_brush_addnew(ToolSettings *ts, const char *name, bool setactive) -{ - bGPDbrush *brush; - - /* check that list is ok */ - if (ts == NULL) { - return NULL; - } - - /* allocate memory and add to end of list */ - brush = MEM_callocN(sizeof(bGPDbrush), "bGPDbrush"); - - /* add to datablock */ - BLI_addtail(&ts->gp_brushes, brush); - - /* set basic settings */ - brush->thickness = 3; - brush->draw_smoothlvl = 1; - brush->flag |= GP_BRUSH_USE_PRESSURE; - brush->draw_sensitivity = 1.0f; - brush->draw_strength = 1.0f; - brush->flag |= GP_BRUSH_USE_STENGTH_PRESSURE; - brush->draw_jitter = 0.0f; - brush->flag |= GP_BRUSH_USE_JITTER_PRESSURE; - - /* curves */ - brush->cur_sensitivity = curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); - brush->cur_strength = curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); - brush->cur_jitter = curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); - - /* auto-name */ - BLI_strncpy(brush->info, name, sizeof(brush->info)); - BLI_uniquename(&ts->gp_brushes, brush, DATA_("GP_Brush"), '.', offsetof(bGPDbrush, info), sizeof(brush->info)); - - /* make this one the active one */ - if (setactive) { - BKE_gpencil_brush_setactive(ts, brush); - } - /* return brush */ - return brush; -} +/* ************************************************** */ +/* Data Duplication */ -/* add a new gp-palettecolor and make it the active */ -bGPDpalettecolor *BKE_gpencil_palettecolor_addnew(bGPDpalette *palette, const char *name, bool setactive) +/* make a copy of a given gpencil weights */ +void BKE_gpencil_stroke_weights_duplicate(bGPDstroke *gps_src, bGPDstroke *gps_dst) { - bGPDpalettecolor *palcolor; - - /* check that list is ok */ - if (palette == NULL) { - return NULL; + if (gps_src == NULL) { + return; } + BLI_assert(gps_src->totpoints == gps_dst->totpoints); - /* allocate memory and add to end of list */ - palcolor = MEM_callocN(sizeof(bGPDpalettecolor), "bGPDpalettecolor"); - - /* add to datablock */ - BLI_addtail(&palette->colors, palcolor); - - /* set basic settings */ - palcolor->flag |= PC_COLOR_HQ_FILL; - copy_v4_v4(palcolor->color, U.gpencil_new_layer_col); - ARRAY_SET_ITEMS(palcolor->fill, 1.0f, 1.0f, 1.0f); + if ((gps_src->dvert == NULL) || (gps_dst->dvert == NULL)) { + return; + } - /* auto-name */ - BLI_strncpy(palcolor->info, name, sizeof(palcolor->info)); - BLI_uniquename(&palette->colors, palcolor, DATA_("Color"), '.', offsetof(bGPDpalettecolor, info), - sizeof(palcolor->info)); + for (int i = 0; i < gps_src->totpoints; i++) { + MDeformVert *dvert_src = &gps_src->dvert[i]; + MDeformVert *dvert_dst = &gps_dst->dvert[i]; + if (dvert_src->totweight > 0) { + dvert_dst->dw = MEM_dupallocN(dvert_src->dw); + } + else { + dvert_dst->dw = NULL; + } - /* make this one the active one */ - if (setactive) { - BKE_gpencil_palettecolor_setactive(palette, palcolor); } - - /* return palette color */ - return palcolor; } -/* add a new gp-datablock */ -bGPdata *BKE_gpencil_data_addnew(Main *bmain, const char name[]) +/* make a copy of a given gpencil stroke */ +bGPDstroke *BKE_gpencil_stroke_duplicate(bGPDstroke *gps_src) { - bGPdata *gpd; + bGPDstroke *gps_dst = NULL; - /* allocate memory for a new block */ - gpd = BKE_libblock_alloc(bmain, ID_GD, name, 0); + gps_dst = MEM_dupallocN(gps_src); + gps_dst->prev = gps_dst->next = NULL; - /* initial settings */ - gpd->flag = (GP_DATA_DISPINFO | GP_DATA_EXPAND); + gps_dst->points = MEM_dupallocN(gps_src->points); + + gps_dst->dvert = MEM_dupallocN(gps_src->dvert); + BKE_gpencil_stroke_weights_duplicate(gps_src, gps_dst); - /* for now, stick to view is also enabled by default - * since this is more useful... + /* Don't clear triangles, so that modifier evaluation can just use + * this without extra work first. Most places that need to force + * this data to get recalculated will destroy the data anyway though. */ - gpd->flag |= GP_DATA_VIEWALIGN; + gps_dst->triangles = MEM_dupallocN(gps_dst->triangles); + /* gps_dst->flag |= GP_STROKE_RECALC_CACHES; */ - return gpd; + /* return new stroke */ + return gps_dst; } -/* -------- Data Duplication ---------- */ - /* make a copy of a given gpencil frame */ bGPDframe *BKE_gpencil_frame_duplicate(const bGPDframe *gpf_src) { - bGPDstroke *gps_dst; + bGPDstroke *gps_dst = NULL; bGPDframe *gpf_dst; /* error checking */ @@ -660,11 +605,8 @@ bGPDframe *BKE_gpencil_frame_duplicate(const bGPDframe *gpf_src) /* copy strokes */ BLI_listbase_clear(&gpf_dst->strokes); for (bGPDstroke *gps_src = gpf_src->strokes.first; gps_src; gps_src = gps_src->next) { - /* make copy of source stroke, then adjust pointer to points too */ - gps_dst = MEM_dupallocN(gps_src); - gps_dst->points = MEM_dupallocN(gps_src->points); - gps_dst->triangles = MEM_dupallocN(gps_src->triangles); - gps_dst->flag |= GP_STROKE_RECALC_CACHES; + /* make copy of source stroke */ + gps_dst = BKE_gpencil_stroke_duplicate(gps_src); BLI_addtail(&gpf_dst->strokes, gps_dst); } @@ -672,55 +614,24 @@ bGPDframe *BKE_gpencil_frame_duplicate(const bGPDframe *gpf_src) return gpf_dst; } -/* make a copy of a given gpencil brush */ -bGPDbrush *BKE_gpencil_brush_duplicate(const bGPDbrush *brush_src) +/* make a copy of strokes between gpencil frames */ +void BKE_gpencil_frame_copy_strokes(bGPDframe *gpf_src, struct bGPDframe *gpf_dst) { - bGPDbrush *brush_dst; - + bGPDstroke *gps_dst = NULL; /* error checking */ - if (brush_src == NULL) { - return NULL; - } - - /* make a copy of source brush */ - brush_dst = MEM_dupallocN(brush_src); - brush_dst->prev = brush_dst->next = NULL; - /* make a copy of curves */ - brush_dst->cur_sensitivity = curvemapping_copy(brush_src->cur_sensitivity); - brush_dst->cur_strength = curvemapping_copy(brush_src->cur_strength); - brush_dst->cur_jitter = curvemapping_copy(brush_src->cur_jitter); - - /* return new brush */ - return brush_dst; -} - -/* make a copy of a given gpencil palette */ -bGPDpalette *BKE_gpencil_palette_duplicate(const bGPDpalette *palette_src) -{ - bGPDpalette *palette_dst; - const bGPDpalettecolor *palcolor_src; - bGPDpalettecolor *palcolord_dst; - - /* error checking */ - if (palette_src == NULL) { - return NULL; + if ((gpf_src == NULL) || (gpf_dst == NULL)) { + return; } - /* make a copy of source palette */ - palette_dst = MEM_dupallocN(palette_src); - palette_dst->prev = palette_dst->next = NULL; - - /* copy colors */ - BLI_listbase_clear(&palette_dst->colors); - for (palcolor_src = palette_src->colors.first; palcolor_src; palcolor_src = palcolor_src->next) { - /* make a copy of source */ - palcolord_dst = MEM_dupallocN(palcolor_src); - BLI_addtail(&palette_dst->colors, palcolord_dst); + /* copy strokes */ + BLI_listbase_clear(&gpf_dst->strokes); + for (bGPDstroke *gps_src = gpf_src->strokes.first; gps_src; gps_src = gps_src->next) { + /* make copy of source stroke */ + gps_dst = BKE_gpencil_stroke_duplicate(gps_src); + BLI_addtail(&gpf_dst->strokes, gps_dst); } - - /* return new palette */ - return palette_dst; } + /* make a copy of a given gpencil layer */ bGPDlayer *BKE_gpencil_layer_duplicate(const bGPDlayer *gpl_src) { @@ -736,6 +647,7 @@ bGPDlayer *BKE_gpencil_layer_duplicate(const bGPDlayer *gpl_src) /* make a copy of source layer */ gpl_dst = MEM_dupallocN(gpl_src); gpl_dst->prev = gpl_dst->next = NULL; + gpl_dst->runtime.derived_data = NULL; /* copy frames */ BLI_listbase_clear(&gpl_dst->frames); @@ -755,14 +667,22 @@ bGPDlayer *BKE_gpencil_layer_duplicate(const bGPDlayer *gpl_src) /** * Only copy internal data of GreasePencil ID from source to already allocated/initialized destination. - * You probably nerver want to use that directly, use id_copy or BKE_id_copy_ex for typical needs. + * You probably never want to use that directly, use id_copy or BKE_id_copy_ex for typical needs. * * WARNING! This function will not handle ID user count! * * \param flag Copying options (see BKE_library.h's LIB_ID_COPY_... flags for more). */ -void BKE_gpencil_copy_data(Main *UNUSED(bmain), bGPdata *gpd_dst, const bGPdata *gpd_src, const int UNUSED(flag)) +void BKE_gpencil_copy_data(bGPdata *gpd_dst, const bGPdata *gpd_src, const int UNUSED(flag)) { + /* cache data is not duplicated */ + gpd_dst->runtime.batch_cache_data = NULL; + + /* duplicate material array */ + if (gpd_src->mat) { + gpd_dst->mat = MEM_dupallocN(gpd_src->mat); + } + /* copy layers */ BLI_listbase_clear(&gpd_dst->layers); for (const bGPDlayer *gpl_src = gpd_src->layers.first; gpl_src; gpl_src = gpl_src->next) { @@ -771,45 +691,46 @@ void BKE_gpencil_copy_data(Main *UNUSED(bmain), bGPdata *gpd_dst, const bGPdata BLI_addtail(&gpd_dst->layers, gpl_dst); } - /* copy palettes */ - BLI_listbase_clear(&gpd_dst->palettes); - for (const bGPDpalette *palette_src = gpd_src->palettes.first; palette_src; palette_src = palette_src->next) { - bGPDpalette *palette_dst = BKE_gpencil_palette_duplicate(palette_src); /* TODO here too could add unused flags... */ - BLI_addtail(&gpd_dst->palettes, palette_dst); - } +} + +/* Standard API to make a copy of GP datablock, separate from copying its data */ +bGPdata *BKE_gpencil_copy(Main *bmain, const bGPdata *gpd) +{ + bGPdata *gpd_copy; + BKE_id_copy_ex(bmain, &gpd->id, (ID **)&gpd_copy, 0, false); + return gpd_copy; } /* make a copy of a given gpencil datablock */ +// XXX: Should this be deprecated? bGPdata *BKE_gpencil_data_duplicate(Main *bmain, const bGPdata *gpd_src, bool internal_copy) { + bGPdata *gpd_dst; + /* Yuck and super-uber-hyper yuck!!! * Should be replaceable with a no-main copy (LIB_ID_COPY_NO_MAIN etc.), but not sure about it, * so for now keep old code for that one. */ - if (internal_copy) { - const bGPDlayer *gpl_src; - bGPDlayer *gpl_dst; - bGPdata *gpd_dst; + /* error checking */ + if (gpd_src == NULL) { + return NULL; + } + + if (internal_copy) { /* make a straight copy for undo buffers used during stroke drawing */ gpd_dst = MEM_dupallocN(gpd_src); - - /* copy layers */ - BLI_listbase_clear(&gpd_dst->layers); - for (gpl_src = gpd_src->layers.first; gpl_src; gpl_src = gpl_src->next) { - /* make a copy of source layer and its data */ - gpl_dst = BKE_gpencil_layer_duplicate(gpl_src); - BLI_addtail(&gpd_dst->layers, gpl_dst); - } - - /* return new */ - return gpd_dst; } else { BLI_assert(bmain != NULL); - bGPdata *gpd_copy; - BKE_id_copy_ex(bmain, &gpd_src->id, (ID **)&gpd_copy, 0, false); - return gpd_copy; + BKE_id_copy_ex(bmain, &gpd_src->id, (ID **)&gpd_dst, 0, false); + gpd_dst->runtime.batch_cache_data = NULL; } + + /* Copy internal data (layers, etc.) */ + BKE_gpencil_copy_data(gpd_dst, gpd_src, 0); + + /* return new */ + return gpd_dst; } void BKE_gpencil_make_local(Main *bmain, bGPdata *gpd, const bool lib_local) @@ -817,7 +738,8 @@ void BKE_gpencil_make_local(Main *bmain, bGPdata *gpd, const bool lib_local) BKE_id_make_local_generic(bmain, &gpd->id, true, lib_local); } -/* -------- GP-Stroke API --------- */ +/* ************************************************** */ +/* GP Stroke API */ /* ensure selection status of stroke is in sync with its points */ void BKE_gpencil_stroke_sync_selection(bGPDstroke *gps) @@ -842,7 +764,8 @@ void BKE_gpencil_stroke_sync_selection(bGPDstroke *gps) } } -/* -------- GP-Frame API ---------- */ +/* ************************************************** */ +/* GP Frame API */ /* delete the last stroke of the given frame */ void BKE_gpencil_frame_delete_laststroke(bGPDlayer *gpl, bGPDframe *gpf) @@ -855,7 +778,13 @@ void BKE_gpencil_frame_delete_laststroke(bGPDlayer *gpl, bGPDframe *gpf) return; /* free the stroke and its data */ - MEM_freeN(gps->points); + if (gps->points) { + MEM_freeN(gps->points); + } + if (gps->dvert) { + BKE_gpencil_free_stroke_weights(gps); + MEM_freeN(gps->dvert); + } MEM_freeN(gps->triangles); BLI_freelinkN(&gpf->strokes, gps); @@ -866,7 +795,8 @@ void BKE_gpencil_frame_delete_laststroke(bGPDlayer *gpl, bGPDframe *gpf) } } -/* -------- GP-Layer API ---------- */ +/* ************************************************** */ +/* GP Layer API */ /* Check if the given layer is able to be edited or not */ bool gpencil_layer_is_editable(const bGPDlayer *gpl) @@ -1048,8 +978,6 @@ bool BKE_gpencil_layer_delframe(bGPDlayer *gpl, bGPDframe *gpf) */ if (gpl->actframe == gpf) gpl->actframe = gpf->prev; - else - gpl->actframe = NULL; /* free the frame and its data */ changed = BKE_gpencil_free_strokes(gpf); @@ -1103,255 +1031,617 @@ void BKE_gpencil_layer_delete(bGPdata *gpd, bGPDlayer *gpl) /* free layer */ BKE_gpencil_free_frames(gpl); + + /* free icon providing preview of icon color */ + BKE_icon_delete(gpl->runtime.icon_id); + + /* free derived data */ + BKE_gpencil_clear_derived(gpl); + if (gpl->runtime.derived_data) { + BLI_ghash_free(gpl->runtime.derived_data, NULL, NULL); + gpl->runtime.derived_data = NULL; + } + BLI_freelinkN(&gpd->layers, gpl); } -/* ************************************************** */ -/* get the active gp-brush for editing */ -bGPDbrush *BKE_gpencil_brush_getactive(ToolSettings *ts) +Material *BKE_gpencil_get_material_from_brush(Brush *brush) { - bGPDbrush *brush; + Material *ma = NULL; - /* error checking */ - if (ELEM(NULL, ts, ts->gp_brushes.first)) { - return NULL; + if ((brush != NULL) && (brush->gpencil_settings != NULL) && + (brush->gpencil_settings->material != NULL)) + { + ma = brush->gpencil_settings->material; } - /* loop over brushes until found (assume only one active) */ - for (brush = ts->gp_brushes.first; brush; brush = brush->next) { - if (brush->flag & GP_BRUSH_ACTIVE) { - return brush; + return ma; +} + +/* Get active color, and add all default settings if we don't find anything */ +Material *BKE_gpencil_material_ensure(Main *bmain, Object *ob) +{ + Material *ma = NULL; + + /* sanity checks */ + if (ELEM(NULL, bmain, ob)) + return NULL; + + ma = give_current_material(ob, ob->actcol); + if (ma == NULL) { + if (ob->totcol == 0) { + BKE_object_material_slot_add(bmain, ob); } + ma = BKE_material_add_gpencil(bmain, DATA_("Material")); + assign_material(bmain, ob, ma, ob->totcol, BKE_MAT_ASSIGN_USERPREF); + } + else if (ma->gp_style == NULL) { + BKE_material_init_gpencil_settings(ma); } - /* no active brush found */ - return NULL; + return ma; } -/* set the active gp-brush */ -void BKE_gpencil_brush_setactive(ToolSettings *ts, bGPDbrush *active) +/* ************************************************** */ +/* GP Object - Boundbox Support */ + +/** + * Get min/max coordinate bounds for single stroke + * \return Returns whether we found any selected points + */ +bool BKE_gpencil_stroke_minmax( + const bGPDstroke *gps, const bool use_select, + float r_min[3], float r_max[3]) { - bGPDbrush *brush; + const bGPDspoint *pt; + int i; + bool changed = false; - /* error checking */ - if (ELEM(NULL, ts, ts->gp_brushes.first, active)) { - return; - } + if (ELEM(NULL, gps, r_min, r_max)) + return false; - /* loop over brushes deactivating all */ - for (brush = ts->gp_brushes.first; brush; brush = brush->next) { - brush->flag &= ~GP_BRUSH_ACTIVE; + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + if ((use_select == false) || (pt->flag & GP_SPOINT_SELECT)) { + minmax_v3v3_v3(r_min, r_max, &pt->x); + changed = true; + } } - - /* set as active one */ - active->flag |= GP_BRUSH_ACTIVE; + return changed; } -/* delete the active gp-brush */ -void BKE_gpencil_brush_delete(ToolSettings *ts, bGPDbrush *brush) +/* get min/max bounds of all strokes in GP datablock */ +static void gpencil_minmax(bGPdata *gpd, float r_min[3], float r_max[3]) { - /* error checking */ - if (ELEM(NULL, ts, brush)) { + INIT_MINMAX(r_min, r_max); + + if (gpd == NULL) return; - } - /* free curves */ - if (brush->cur_sensitivity) { - curvemapping_free(brush->cur_sensitivity); - } - if (brush->cur_strength) { - curvemapping_free(brush->cur_strength); + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + bGPDframe *gpf = gpl->actframe; + + if (gpf != NULL) { + for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + BKE_gpencil_stroke_minmax(gps, false, r_min, r_max); + } + } } - if (brush->cur_jitter) { - curvemapping_free(brush->cur_jitter); +} + +/* compute center of bounding box */ +void BKE_gpencil_centroid_3D(bGPdata *gpd, float r_centroid[3]) +{ + float min[3], max[3], tot[3]; + + gpencil_minmax(gpd, min, max); + + add_v3_v3v3(tot, min, max); + mul_v3_v3fl(r_centroid, tot, 0.5f); +} + + +/* create bounding box values */ +static void boundbox_gpencil(Object *ob) +{ + BoundBox *bb; + bGPdata *gpd; + float min[3], max[3]; + + if (ob->bb == NULL) { + ob->bb = MEM_callocN(sizeof(BoundBox), "GPencil boundbox"); } - /* free */ - BLI_freelinkN(&ts->gp_brushes, brush); + bb = ob->bb; + gpd = ob->data; + + gpencil_minmax(gpd, min, max); + BKE_boundbox_init_from_minmax(bb, min, max); + + bb->flag &= ~BOUNDBOX_DIRTY; } -/* ************************************************** */ -/* get the active gp-palette for editing */ -bGPDpalette *BKE_gpencil_palette_getactive(bGPdata *gpd) +/* get bounding box */ +BoundBox *BKE_gpencil_boundbox_get(Object *ob) { - bGPDpalette *palette; + bGPdata *gpd; - /* error checking */ - if (ELEM(NULL, gpd, gpd->palettes.first)) { + if (ELEM(NULL, ob, ob->data)) return NULL; - } - /* loop over palettes until found (assume only one active) */ - for (palette = gpd->palettes.first; palette; palette = palette->next) { - if (palette->flag & PL_PALETTE_ACTIVE) - return palette; + gpd = ob->data; + if ((ob->bb) && ((ob->bb->flag & BOUNDBOX_DIRTY) == 0) && + ((gpd->flag & GP_DATA_CACHE_IS_DIRTY) == 0)) + { + return ob->bb; } - /* no active palette found */ - return NULL; + boundbox_gpencil(ob); + + return ob->bb; } -/* set the active gp-palette */ -void BKE_gpencil_palette_setactive(bGPdata *gpd, bGPDpalette *active) -{ - bGPDpalette *palette; +/* ************************************************** */ +/* Apply Transforms */ - /* error checking */ - if (ELEM(NULL, gpd, gpd->palettes.first, active)) { +void BKE_gpencil_transform(bGPdata *gpd, float mat[4][4]) +{ + if (gpd == NULL) return; + + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + /* FIXME: For now, we just skip parented layers. + * Otherwise, we have to update each frame to find + * the current parent position/effects. + */ + if (gpl->parent) { + continue; + } + + for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { + for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + bGPDspoint *pt; + int i; + + for (pt = gps->points, i = 0; i < gps->totpoints; pt++, i++) { + mul_m4_v3(mat, &pt->x); + } + + /* TODO: Do we need to do this? distortion may mean we need to re-triangulate */ + gps->flag |= GP_STROKE_RECALC_CACHES; + gps->tot_triangles = 0; + } + } } - /* loop over palettes deactivating all */ - for (palette = gpd->palettes.first; palette; palette = palette->next) { - palette->flag &= ~PL_PALETTE_ACTIVE; +} + +/* ************************************************** */ +/* GP Object - Vertex Groups */ + +/* remove a vertex group */ +void BKE_gpencil_vgroup_remove(Object *ob, bDeformGroup *defgroup) +{ + bGPdata *gpd = ob->data; + MDeformVert *dvert = NULL; + MDeformWeight *gpw = NULL; + const int def_nr = BLI_findindex(&ob->defbase, defgroup); + + /* Remove points data */ + if (gpd) { + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { + for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + for (int i = 0; i < gps->totpoints; i++) { + dvert = &gps->dvert[i]; + for (int i2 = 0; i2 < dvert->totweight; i2++) { + gpw = &dvert->dw[i2]; + if (gpw->def_nr == def_nr) { + BKE_gpencil_vgroup_remove_point_weight(dvert, def_nr); + } + /* if index is greater, must be moved one back */ + if (gpw->def_nr > def_nr) { + gpw->def_nr--; + } + } + } + } + } + } } - /* set as active one */ - active->flag |= PL_PALETTE_ACTIVE; - /* force color recalc */ - BKE_gpencil_palette_change_strokes(gpd); + /* Remove the group */ + BLI_freelinkN(&ob->defbase, defgroup); } -/* delete the active gp-palette */ -void BKE_gpencil_palette_delete(bGPdata *gpd, bGPDpalette *palette) +/* add a new weight */ +MDeformWeight *BKE_gpencil_vgroup_add_point_weight(MDeformVert *dvert, int index, float weight) { - /* error checking */ - if (ELEM(NULL, gpd, palette)) { - return; + MDeformWeight *new_gpw = NULL; + MDeformWeight *tmp_gpw; + + /* need to verify if was used before to update */ + for (int i = 0; i < dvert->totweight; i++) { + tmp_gpw = &dvert->dw[i]; + if (tmp_gpw->def_nr == index) { + tmp_gpw->weight = weight; + return tmp_gpw; + } } - /* free colors */ - free_gpencil_colors(palette); - BLI_freelinkN(&gpd->palettes, palette); - /* force color recalc */ - BKE_gpencil_palette_change_strokes(gpd); + dvert->totweight++; + if (dvert->totweight == 1) { + dvert->dw = MEM_callocN(sizeof(MDeformWeight), "gp_weight"); + } + else { + dvert->dw = MEM_reallocN(dvert->dw, sizeof(MDeformWeight) * dvert->totweight); + } + new_gpw = &dvert->dw[dvert->totweight - 1]; + new_gpw->def_nr = index; + new_gpw->weight = weight; + + return new_gpw; } -/* Set all strokes to recalc the palette color */ -void BKE_gpencil_palette_change_strokes(bGPdata *gpd) +/* return the weight if use index or -1*/ +float BKE_gpencil_vgroup_use_index(MDeformVert *dvert, int index) { - bGPDlayer *gpl; - bGPDframe *gpf; - bGPDstroke *gps; + MDeformWeight *gpw; + for (int i = 0; i < dvert->totweight; i++) { + gpw = &dvert->dw[i]; + if (gpw->def_nr == index) { + return gpw->weight; + } + } + return -1.0f; +} - for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { - for (gpf = gpl->frames.first; gpf; gpf = gpf->next) { - for (gps = gpf->strokes.first; gps; gps = gps->next) { - gps->flag |= GP_STROKE_RECALC_COLOR; - } +/* add a new weight */ +bool BKE_gpencil_vgroup_remove_point_weight(MDeformVert *dvert, int index) +{ + int e = 0; + + if (BKE_gpencil_vgroup_use_index(dvert, index) < 0.0f) { + return false; + } + + /* if the array get empty, exit */ + if (dvert->totweight == 1) { + dvert->totweight = 0; + MEM_SAFE_FREE(dvert->dw); + return true; + } + + /* realloc weights */ + MDeformWeight *tmp = MEM_dupallocN(dvert->dw); + MEM_SAFE_FREE(dvert->dw); + dvert->dw = MEM_callocN(sizeof(MDeformWeight) * dvert->totweight - 1, "gp_weights"); + + for (int x = 0; x < dvert->totweight; x++) { + MDeformWeight *gpw = &tmp[e]; + MDeformWeight *final_gpw = &dvert->dw[e]; + if (gpw->def_nr != index) { + final_gpw->def_nr = gpw->def_nr; + final_gpw->weight = gpw->weight; + e++; } } + MEM_SAFE_FREE(tmp); + dvert->totweight--; + + return true; } -/* get the active gp-palettecolor for editing */ -bGPDpalettecolor *BKE_gpencil_palettecolor_getactive(bGPDpalette *palette) +/* ************************************************** */ + +/** + * Apply smooth to stroke point + * \param gps Stroke to smooth + * \param i Point index + * \param inf Amount of smoothing to apply + */ +bool BKE_gpencil_smooth_stroke(bGPDstroke *gps, int i, float inf) { - bGPDpalettecolor *palcolor; + bGPDspoint *pt = &gps->points[i]; + // float pressure = 0.0f; + float sco[3] = { 0.0f }; - /* error checking */ - if (ELEM(NULL, palette, palette->colors.first)) { - return NULL; + /* Do nothing if not enough points to smooth out */ + if (gps->totpoints <= 2) { + return false; + } + + /* Only affect endpoints by a fraction of the normal strength, + * to prevent the stroke from shrinking too much + */ + if ((i == 0) || (i == gps->totpoints - 1)) { + inf *= 0.1f; } - /* loop over colors until found (assume only one active) */ - for (palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) { - if (palcolor->flag & PC_COLOR_ACTIVE) { - return palcolor; + /* Compute smoothed coordinate by taking the ones nearby */ + /* XXX: This is potentially slow, and suffers from accumulation error as earlier points are handled before later ones */ + { + // XXX: this is hardcoded to look at 2 points on either side of the current one (i.e. 5 items total) + const int steps = 2; + const float average_fac = 1.0f / (float)(steps * 2 + 1); + int step; + + /* add the point itself */ + madd_v3_v3fl(sco, &pt->x, average_fac); + + /* n-steps before/after current point */ + // XXX: review how the endpoints are treated by this algorithm + // XXX: falloff measures should also introduce some weighting variations, so that further-out points get less weight + for (step = 1; step <= steps; step++) { + bGPDspoint *pt1, *pt2; + int before = i - step; + int after = i + step; + + CLAMP_MIN(before, 0); + CLAMP_MAX(after, gps->totpoints - 1); + + pt1 = &gps->points[before]; + pt2 = &gps->points[after]; + + /* add both these points to the average-sum (s += p[i]/n) */ + madd_v3_v3fl(sco, &pt1->x, average_fac); + madd_v3_v3fl(sco, &pt2->x, average_fac); + } } - /* no active color found */ - return NULL; + /* Based on influence factor, blend between original and optimal smoothed coordinate */ + interp_v3_v3v3(&pt->x, &pt->x, sco, inf); + + return true; } -/* get the gp-palettecolor looking for name */ -bGPDpalettecolor *BKE_gpencil_palettecolor_getbyname(bGPDpalette *palette, char *name) + +/** + * Apply smooth for strength to stroke point */ +bool BKE_gpencil_smooth_stroke_strength(bGPDstroke *gps, int point_index, float influence) { - /* error checking */ - if (ELEM(NULL, palette, name)) { - return NULL; + bGPDspoint *ptb = &gps->points[point_index]; + + /* Do nothing if not enough points */ + if (gps->totpoints <= 2) { + return false; } - return BLI_findstring(&palette->colors, name, offsetof(bGPDpalettecolor, info)); + /* Compute theoretical optimal value using distances */ + bGPDspoint *pta, *ptc; + int before = point_index - 1; + int after = point_index + 1; + + CLAMP_MIN(before, 0); + CLAMP_MAX(after, gps->totpoints - 1); + + pta = &gps->points[before]; + ptc = &gps->points[after]; + + /* the optimal value is the corresponding to the interpolation of the strength + * at the distance of point b + */ + const float fac = line_point_factor_v3(&ptb->x, &pta->x, &ptc->x); + const float optimal = (1.0f - fac) * pta->strength + fac * ptc->strength; + + /* Based on influence factor, blend between original and optimal */ + ptb->strength = (1.0f - influence) * ptb->strength + influence * optimal; + + return true; } -/* Change color name in all strokes */ -void BKE_gpencil_palettecolor_changename(bGPdata *gpd, char *oldname, const char *newname) +/** + * Apply smooth for thickness to stroke point (use pressure) */ +bool BKE_gpencil_smooth_stroke_thickness(bGPDstroke *gps, int point_index, float influence) { - bGPDlayer *gpl; - bGPDframe *gpf; - bGPDstroke *gps; + bGPDspoint *ptb = &gps->points[point_index]; - /* Sanity checks (gpd may not be set in the RNA pointers sometimes) */ - if (ELEM(NULL, gpd, oldname, newname)) - return; + /* Do nothing if not enough points */ + if (gps->totpoints <= 2) { + return false; + } - for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { - for (gpf = gpl->frames.first; gpf; gpf = gpf->next) { - for (gps = gpf->strokes.first; gps; gps = gps->next) { - if (STREQ(gps->colorname, oldname)) { - BLI_strncpy(gps->colorname, newname, sizeof(gps->colorname)); - } + /* Compute theoretical optimal value using distances */ + bGPDspoint *pta, *ptc; + int before = point_index - 1; + int after = point_index + 1; + + CLAMP_MIN(before, 0); + CLAMP_MAX(after, gps->totpoints - 1); + + pta = &gps->points[before]; + ptc = &gps->points[after]; + + /* the optimal value is the corresponding to the interpolation of the pressure + * at the distance of point b + */ + float fac = line_point_factor_v3(&ptb->x, &pta->x, &ptc->x); + float optimal = interpf(ptc->pressure, pta->pressure, fac); + + /* Based on influence factor, blend between original and optimal */ + ptb->pressure = interpf(optimal, ptb->pressure, influence); + + return true; +} + +/** +* Apply smooth for UV rotation to stroke point (use pressure) */ +bool BKE_gpencil_smooth_stroke_uv(bGPDstroke *gps, int point_index, float influence) +{ + bGPDspoint *ptb = &gps->points[point_index]; + + /* Do nothing if not enough points */ + if (gps->totpoints <= 2) { + return false; + } + + /* Compute theoretical optimal value */ + bGPDspoint *pta, *ptc; + int before = point_index - 1; + int after = point_index + 1; + + CLAMP_MIN(before, 0); + CLAMP_MAX(after, gps->totpoints - 1); + + pta = &gps->points[before]; + ptc = &gps->points[after]; + + /* the optimal value is the corresponding to the interpolation of the pressure + * at the distance of point b + */ + float fac = line_point_factor_v3(&ptb->x, &pta->x, &ptc->x); + float optimal = interpf(ptc->uv_rot, pta->uv_rot, fac); + + /* Based on influence factor, blend between original and optimal */ + ptb->uv_rot = interpf(optimal, ptb->uv_rot, influence); + CLAMP(ptb->uv_rot, -M_PI_2, M_PI_2); + + return true; +} + +/** + * Get range of selected frames in layer. + * Always the active frame is considered as selected, so if no more selected the range + * will be equal to the current active frame. + * \param gpl Layer + * \param r_initframe Number of first selected frame + * \param r_endframe Number of last selected frame + */ +void BKE_gpencil_get_range_selected(bGPDlayer *gpl, int *r_initframe, int *r_endframe) +{ + *r_initframe = gpl->actframe->framenum; + *r_endframe = gpl->actframe->framenum; + + for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { + if (gpf->flag & GP_FRAME_SELECT) { + if (gpf->framenum < *r_initframe) { + *r_initframe = gpf->framenum; + } + if (gpf->framenum > *r_endframe) { + *r_endframe = gpf->framenum; } } } +} +/** + * Get Falloff factor base on frame range + * \param gpf Frame + * \param actnum Number of active frame in layer + * \param f_init Number of first selected frame + * \param f_end Number of last selected frame + * \param cur_falloff Curve with falloff factors + */ +float BKE_gpencil_multiframe_falloff_calc(bGPDframe *gpf, int actnum, int f_init, int f_end, CurveMapping *cur_falloff) +{ + float fnum = 0.5f; /* default mid curve */ + float value; + + /* frames to the right of the active frame */ + if (gpf->framenum < actnum) { + fnum = (float)(gpf->framenum - f_init) / (actnum - f_init); + fnum *= 0.5f; + value = curvemapping_evaluateF(cur_falloff, 0, fnum); + } + /* frames to the left of the active frame */ + else if (gpf->framenum > actnum) { + fnum = (float)(gpf->framenum - actnum) / (f_end - actnum); + fnum *= 0.5f; + value = curvemapping_evaluateF(cur_falloff, 0, fnum + 0.5f); + } + else { + value = 1.0f; + } + + return value; } -/* Delete all strokes of the color */ -void BKE_gpencil_palettecolor_delete_strokes(struct bGPdata *gpd, char *name) +/* remove strokes using a material */ +void BKE_gpencil_material_index_remove(bGPdata *gpd, int index) { - bGPDlayer *gpl; - bGPDframe *gpf; bGPDstroke *gps, *gpsn; - /* Sanity checks (gpd may not be set in the RNA pointers sometimes) */ - if (ELEM(NULL, gpd, name)) - return; - - for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { - for (gpf = gpl->frames.first; gpf; gpf = gpf->next) { - for (gps = gpf->strokes.first; gps; gps = gpsn) { - gpsn = gps->next; - - if (STREQ(gps->colorname, name)) { - if (gps->points) MEM_freeN(gps->points); - if (gps->triangles) MEM_freeN(gps->triangles); - BLI_freelinkN(&gpf->strokes, gps); + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { + for (gps = gpf->strokes.first; gps; gps = gpsn) { + gpsn = gps->next; + if (gps->mat_nr == index) { + if (gps->points) { + MEM_freeN(gps->points); + } + if (gps->dvert) { + BKE_gpencil_free_stroke_weights(gps); + MEM_freeN(gps->dvert); + } + if (gps->triangles) MEM_freeN(gps->triangles); + BLI_freelinkN(&gpf->strokes, gps); + } + else { + /* reassign strokes */ + if (gps->mat_nr > index) { + gps->mat_nr--; + } + } } } } - } - } -/* set the active gp-palettecolor */ -void BKE_gpencil_palettecolor_setactive(bGPDpalette *palette, bGPDpalettecolor *active) +void BKE_gpencil_material_remap(struct bGPdata *gpd, const unsigned int *remap, unsigned int remap_len) { - bGPDpalettecolor *palcolor; - - /* error checking */ - if (ELEM(NULL, palette, palette->colors.first, active)) { - return; + const short remap_len_short = (short)remap_len; + +#define MAT_NR_REMAP(n) \ + if (n < remap_len_short) { \ + BLI_assert(n >= 0 && remap[n] < remap_len_short); \ + n = remap[n]; \ + } ((void)0) + + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { + for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + /* reassign strokes */ + MAT_NR_REMAP(gps->mat_nr); + } + } } - /* loop over colors deactivating all */ - for (palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) { - palcolor->flag &= ~PC_COLOR_ACTIVE; +#undef MAT_NR_REMAP + +} + +/* statistics functions */ +void BKE_gpencil_stats_update(bGPdata *gpd) +{ + gpd->totlayer = 0; + gpd->totframe = 0; + gpd->totstroke = 0; + gpd->totpoint = 0; + + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + gpd->totlayer++; + for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { + gpd->totframe++; + for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + gpd->totstroke++; + gpd->totpoint += gps->totpoints; + } + } } - /* set as active one */ - active->flag |= PC_COLOR_ACTIVE; } -/* delete the active gp-palettecolor */ -void BKE_gpencil_palettecolor_delete(bGPDpalette *palette, bGPDpalettecolor *palcolor) +/* get material index */ +int BKE_gpencil_get_material_index(Object *ob, Material *ma) { - /* error checking */ - if (ELEM(NULL, palette, palcolor)) { - return; + short *totcol = give_totcolp(ob); + Material *read_ma = NULL; + for (short i = 0; i < *totcol; i++) { + read_ma = give_current_material(ob, i + 1); + if (ma == read_ma) { + return i + 1; + } } - /* free */ - BLI_freelinkN(&palette->colors, palcolor); + return 0; } diff --git a/source/blender/blenkernel/intern/gpencil_modifier.c b/source/blender/blenkernel/intern/gpencil_modifier.c new file mode 100644 index 00000000000..d1c455f64e1 --- /dev/null +++ b/source/blender/blenkernel/intern/gpencil_modifier.c @@ -0,0 +1,687 @@ +/* + * ***** 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. + * + * The Original Code is Copyright (C) 2017, Blender Foundation + * This is a new part of Blender + * + * Contributor(s): Antonio Vazquez + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/blenkernel/intern/gpencil_modifier.c + * \ingroup bke + */ + + +#include <stdio.h> + +#include "MEM_guardedalloc.h" + +#include "BLI_blenlib.h" +#include "BLI_utildefines.h" +#include "BLI_math_vector.h" +#include "BLI_string_utils.h" + +#include "BLT_translation.h" + +#include "DNA_meshdata_types.h" +#include "DNA_scene_types.h" +#include "DNA_object_types.h" +#include "DNA_gpencil_types.h" +#include "DNA_gpencil_modifier_types.h" + +#include "BKE_global.h" +#include "BKE_library.h" +#include "BKE_library_query.h" +#include "BKE_gpencil.h" +#include "BKE_lattice.h" +#include "BKE_gpencil_modifier.h" +#include "BKE_object.h" + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_query.h" + +#include "MOD_gpencil_modifiertypes.h" + +static GpencilModifierTypeInfo *modifier_gpencil_types[NUM_GREASEPENCIL_MODIFIER_TYPES] = { NULL }; + +/* *************************************************** */ +/* Geometry Utilities */ + +/* calculate stroke normal using some points */ +void BKE_gpencil_stroke_normal(const bGPDstroke *gps, float r_normal[3]) +{ + if (gps->totpoints < 3) { + zero_v3(r_normal); + return; + } + + bGPDspoint *points = gps->points; + int totpoints = gps->totpoints; + + const bGPDspoint *pt0 = &points[0]; + const bGPDspoint *pt1 = &points[1]; + const bGPDspoint *pt3 = &points[(int)(totpoints * 0.75)]; + + float vec1[3]; + float vec2[3]; + + /* initial vector (p0 -> p1) */ + sub_v3_v3v3(vec1, &pt1->x, &pt0->x); + + /* point vector at 3/4 */ + sub_v3_v3v3(vec2, &pt3->x, &pt0->x); + + /* vector orthogonal to polygon plane */ + cross_v3_v3v3(r_normal, vec1, vec2); + + /* Normalize vector */ + normalize_v3(r_normal); +} + +/* Get points of stroke always flat to view not affected by camera view or view position */ +static void gpencil_stroke_project_2d(const bGPDspoint *points, int totpoints, vec2f *points2d) +{ + const bGPDspoint *pt0 = &points[0]; + const bGPDspoint *pt1 = &points[1]; + const bGPDspoint *pt3 = &points[(int)(totpoints * 0.75)]; + + float locx[3]; + float locy[3]; + float loc3[3]; + float normal[3]; + + /* local X axis (p0 -> p1) */ + sub_v3_v3v3(locx, &pt1->x, &pt0->x); + + /* point vector at 3/4 */ + sub_v3_v3v3(loc3, &pt3->x, &pt0->x); + + /* vector orthogonal to polygon plane */ + cross_v3_v3v3(normal, locx, loc3); + + /* local Y axis (cross to normal/x axis) */ + cross_v3_v3v3(locy, normal, locx); + + /* Normalize vectors */ + normalize_v3(locx); + normalize_v3(locy); + + /* Get all points in local space */ + for (int i = 0; i < totpoints; i++) { + const bGPDspoint *pt = &points[i]; + float loc[3]; + + /* Get local space using first point as origin */ + sub_v3_v3v3(loc, &pt->x, &pt0->x); + + vec2f *point = &points2d[i]; + point->x = dot_v3v3(loc, locx); + point->y = dot_v3v3(loc, locy); + } + +} + +/* Stroke Simplify ------------------------------------- */ + +/* Reduce a series of points to a simplified version, but + * maintains the general shape of the series + * + * Ramer - Douglas - Peucker algorithm + * by http ://en.wikipedia.org/wiki/Ramer-Douglas-Peucker_algorithm + */ +static void gpencil_rdp_stroke(bGPDstroke *gps, vec2f *points2d, float epsilon) +{ + vec2f *old_points2d = points2d; + int totpoints = gps->totpoints; + char *marked = NULL; + char work; + + int start = 1; + int end = gps->totpoints - 2; + + marked = MEM_callocN(totpoints, "GP marked array"); + marked[start] = 1; + marked[end] = 1; + + work = 1; + int totmarked = 0; + /* while still reducing */ + while (work) { + int ls, le; + work = 0; + + ls = start; + le = start + 1; + + /* while not over interval */ + while (ls < end) { + int max_i = 0; + float v1[2]; + /* divided to get more control */ + float max_dist = epsilon / 10.0f; + + /* find the next marked point */ + while (marked[le] == 0) { + le++; + } + + /* perpendicular vector to ls-le */ + v1[1] = old_points2d[le].x - old_points2d[ls].x; + v1[0] = old_points2d[ls].y - old_points2d[le].y; + + for (int i = ls + 1; i < le; i++) { + float mul; + float dist; + float v2[2]; + + v2[0] = old_points2d[i].x - old_points2d[ls].x; + v2[1] = old_points2d[i].y - old_points2d[ls].y; + + if (v2[0] == 0 && v2[1] == 0) { + continue; + } + + mul = (float)(v1[0] * v2[0] + v1[1] * v2[1]) / (float)(v2[0] * v2[0] + v2[1] * v2[1]); + + dist = mul * mul * (v2[0] * v2[0] + v2[1] * v2[1]); + + if (dist > max_dist) { + max_dist = dist; + max_i = i; + } + } + + if (max_i != 0) { + work = 1; + marked[max_i] = 1; + totmarked++; + } + + ls = le; + le = ls + 1; + } + } + + /* adding points marked */ + bGPDspoint *old_points = MEM_dupallocN(gps->points); + MDeformVert *old_dvert = MEM_dupallocN(gps->dvert); + + /* resize gps */ + gps->flag |= GP_STROKE_RECALC_CACHES; + gps->tot_triangles = 0; + + int j = 0; + for (int i = 0; i < totpoints; i++) { + bGPDspoint *pt_src = &old_points[i]; + bGPDspoint *pt = &gps->points[j]; + + MDeformVert *dvert_src = &old_dvert[i]; + MDeformVert *dvert = &gps->dvert[j]; + + if ((marked[i]) || (i == 0) || (i == totpoints - 1)) { + memcpy(pt, pt_src, sizeof(bGPDspoint)); + memcpy(dvert, dvert_src, sizeof(MDeformVert)); + j++; + } + else { + BKE_gpencil_free_point_weights(dvert_src); + } + } + + gps->totpoints = j; + + MEM_SAFE_FREE(old_points); + MEM_SAFE_FREE(old_dvert); + MEM_SAFE_FREE(marked); +} + +/* Simplify stroke using Ramer-Douglas-Peucker algorithm */ +void BKE_gpencil_simplify_stroke(bGPDstroke *gps, float factor) +{ + /* first create temp data and convert points to 2D */ + vec2f *points2d = MEM_mallocN(sizeof(vec2f) * gps->totpoints, "GP Stroke temp 2d points"); + + /* for some old files, the weights array could not be initializated */ + if (gps->dvert == NULL) { + gps->dvert = MEM_callocN(sizeof(MDeformVert) * gps->totpoints, "gp_stroke_weights"); + } + + gpencil_stroke_project_2d(gps->points, gps->totpoints, points2d); + + gpencil_rdp_stroke(gps, points2d, factor); + + MEM_SAFE_FREE(points2d); +} + +/* Simplify alternate vertex of stroke except extrems */ +void BKE_gpencil_simplify_fixed(bGPDstroke *gps) +{ + if (gps->totpoints < 5) { + return; + } + + /* for some old files, the weights array could not be initializated */ + if (gps->dvert == NULL) { + gps->dvert = MEM_callocN(sizeof(MDeformVert) * gps->totpoints, "gp_stroke_weights"); + } + + /* save points */ + bGPDspoint *old_points = MEM_dupallocN(gps->points); + MDeformVert *old_dvert = MEM_dupallocN(gps->dvert); + + /* resize gps */ + int newtot = (gps->totpoints - 2) / 2; + if (((gps->totpoints - 2) % 2) > 0) { + newtot++; + } + newtot += 2; + + gps->points = MEM_recallocN(gps->points, sizeof(*gps->points) * newtot); + gps->dvert = MEM_recallocN(gps->dvert, sizeof(*gps->dvert) * newtot); + gps->flag |= GP_STROKE_RECALC_CACHES; + gps->tot_triangles = 0; + + int j = 0; + for (int i = 0; i < gps->totpoints; i++) { + bGPDspoint *pt_src = &old_points[i]; + bGPDspoint *pt = &gps->points[j]; + + MDeformVert *dvert_src = &old_dvert[i]; + MDeformVert *dvert = &gps->dvert[j]; + + if ((i == 0) || (i == gps->totpoints - 1) || ((i % 2) > 0.0)) { + memcpy(pt, pt_src, sizeof(bGPDspoint)); + memcpy(dvert, dvert_src, sizeof(MDeformVert)); + j++; + } + else { + BKE_gpencil_free_point_weights(dvert_src); + } + } + + gps->totpoints = j; + + MEM_SAFE_FREE(old_points); + MEM_SAFE_FREE(old_dvert); +} + +/* *************************************************** */ +/* Modifier Utilities */ + +/* Lattice Modifier ---------------------------------- */ +/* Usually, evaluation of the lattice modifier is self-contained. + * However, since GP's modifiers operate on a per-stroke basis, + * we need to these two extra functions that called before/after + * each loop over all the geometry being evaluated. + */ + +/* init lattice deform data */ +void BKE_gpencil_lattice_init(Object *ob) +{ + GpencilModifierData *md; + for (md = ob->greasepencil_modifiers.first; md; md = md->next) { + if (md->type == eGpencilModifierType_Lattice) { + LatticeGpencilModifierData *mmd = (LatticeGpencilModifierData *)md; + Object *latob = NULL; + + latob = mmd->object; + if ((!latob) || (latob->type != OB_LATTICE)) { + return; + } + if (mmd->cache_data) { + end_latt_deform((struct LatticeDeformData *)mmd->cache_data); + } + + /* init deform data */ + mmd->cache_data = (struct LatticeDeformData *)init_latt_deform(latob, ob); + } + } +} + +/* clear lattice deform data */ +void BKE_gpencil_lattice_clear(Object *ob) +{ + GpencilModifierData *md; + for (md = ob->greasepencil_modifiers.first; md; md = md->next) { + if (md->type == eGpencilModifierType_Lattice) { + LatticeGpencilModifierData *mmd = (LatticeGpencilModifierData *)md; + if ((mmd) && (mmd->cache_data)) { + end_latt_deform((struct LatticeDeformData *)mmd->cache_data); + mmd->cache_data = NULL; + } + } + } +} + +/* *************************************************** */ +/* Modifier Methods - Evaluation Loops, etc. */ + +/* check if exist geometry modifiers */ +bool BKE_gpencil_has_geometry_modifiers(Object *ob) +{ + GpencilModifierData *md; + for (md = ob->greasepencil_modifiers.first; md; md = md->next) { + const GpencilModifierTypeInfo *mti = BKE_gpencil_modifierType_getInfo(md->type); + + if (mti && mti->generateStrokes) { + return true; + } + } + return false; +} + +/* apply stroke modifiers */ +void BKE_gpencil_stroke_modifiers(Depsgraph *depsgraph, Object *ob, bGPDlayer *gpl, bGPDframe *UNUSED(gpf), bGPDstroke *gps, bool is_render) +{ + GpencilModifierData *md; + bGPdata *gpd = ob->data; + const bool is_edit = GPENCIL_ANY_EDIT_MODE(gpd); + + for (md = ob->greasepencil_modifiers.first; md; md = md->next) { + if (GPENCIL_MODIFIER_ACTIVE(md, is_render)) { + const GpencilModifierTypeInfo *mti = BKE_gpencil_modifierType_getInfo(md->type); + + if (GPENCIL_MODIFIER_EDIT(md, is_edit)) { + continue; + } + + if (mti && mti->deformStroke) { + mti->deformStroke(md, depsgraph, ob, gpl, gps); + } + } + } +} + +/* apply stroke geometry modifiers */ +void BKE_gpencil_geometry_modifiers(Depsgraph *depsgraph, Object *ob, bGPDlayer *gpl, bGPDframe *gpf, bool is_render) +{ + GpencilModifierData *md; + bGPdata *gpd = ob->data; + const bool is_edit = GPENCIL_ANY_EDIT_MODE(gpd); + + for (md = ob->greasepencil_modifiers.first; md; md = md->next) { + if (GPENCIL_MODIFIER_ACTIVE(md, is_render)) { + const GpencilModifierTypeInfo *mti = BKE_gpencil_modifierType_getInfo(md->type); + + if (GPENCIL_MODIFIER_EDIT(md, is_edit)) { + continue; + } + + if (mti->generateStrokes) { + mti->generateStrokes(md, depsgraph, ob, gpl, gpf); + } + } + } +} + +/* *************************************************** */ + +void BKE_gpencil_eval_geometry(Depsgraph *depsgraph, + bGPdata *gpd) +{ + DEG_debug_print_eval(depsgraph, __func__, gpd->id.name, gpd); + int ctime = (int)DEG_get_ctime(depsgraph); + + /* update active frame */ + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + gpl->actframe = BKE_gpencil_layer_getframe(gpl, ctime, GP_GETFRAME_USE_PREV); + } + + /* TODO: Move "derived_gpf" logic here from DRW_gpencil_populate_datablock()? + * This would be better than inventing our own logic for this stuff... + */ + + /* TODO: Move the following code to "BKE_gpencil_eval_done()" (marked as an exit node) + * later when there's more happening here. For now, let's just keep this in here to avoid + * needing to have one more node slowing down evaluation... + */ + if (DEG_is_active(depsgraph)) { + bGPdata *gpd_orig = (bGPdata *)DEG_get_original_id(&gpd->id); + + /* sync "actframe" changes back to main-db too, + * so that editing tools work with copy-on-write + * when the current frame changes + */ + for (bGPDlayer *gpl = gpd_orig->layers.first; gpl; gpl = gpl->next) { + gpl->actframe = BKE_gpencil_layer_getframe(gpl, ctime, GP_GETFRAME_USE_PREV); + } + } +} + +void BKE_gpencil_modifier_init(void) +{ + /* Initialize modifier types */ + gpencil_modifier_type_init(modifier_gpencil_types); /* MOD_gpencil_util.c */ +} + +GpencilModifierData *BKE_gpencil_modifier_new(int type) +{ + const GpencilModifierTypeInfo *mti = BKE_gpencil_modifierType_getInfo(type); + GpencilModifierData *md = MEM_callocN(mti->struct_size, mti->struct_name); + + /* note, this name must be made unique later */ + BLI_strncpy(md->name, DATA_(mti->name), sizeof(md->name)); + + md->type = type; + md->mode = eGpencilModifierMode_Realtime | eGpencilModifierMode_Render | eGpencilModifierMode_Expanded; + md->flag = eGpencilModifierFlag_StaticOverride_Local; + + if (mti->flags & eGpencilModifierTypeFlag_EnableInEditmode) + md->mode |= eGpencilModifierMode_Editmode; + + if (mti->initData) mti->initData(md); + + return md; +} + +static void modifier_free_data_id_us_cb(void *UNUSED(userData), Object *UNUSED(ob), ID **idpoin, int cb_flag) +{ + ID *id = *idpoin; + if (id != NULL && (cb_flag & IDWALK_CB_USER) != 0) { + id_us_min(id); + } +} + +void BKE_gpencil_modifier_free_ex(GpencilModifierData *md, const int flag) +{ + const GpencilModifierTypeInfo *mti = BKE_gpencil_modifierType_getInfo(md->type); + + if ((flag & LIB_ID_CREATE_NO_USER_REFCOUNT) == 0) { + if (mti->foreachIDLink) { + mti->foreachIDLink(md, NULL, modifier_free_data_id_us_cb, NULL); + } + else if (mti->foreachObjectLink) { + mti->foreachObjectLink(md, NULL, (GreasePencilObjectWalkFunc)modifier_free_data_id_us_cb, NULL); + } + } + + if (mti->freeData) mti->freeData(md); + if (md->error) MEM_freeN(md->error); + + MEM_freeN(md); +} + +void BKE_gpencil_modifier_free(GpencilModifierData *md) +{ + BKE_gpencil_modifier_free_ex(md, 0); +} + +/* check unique name */ +bool BKE_gpencil_modifier_unique_name(ListBase *modifiers, GpencilModifierData *gmd) +{ + if (modifiers && gmd) { + const GpencilModifierTypeInfo *gmti = BKE_gpencil_modifierType_getInfo(gmd->type); + return BLI_uniquename(modifiers, gmd, DATA_(gmti->name), '.', offsetof(GpencilModifierData, name), sizeof(gmd->name)); + } + return false; +} + +bool BKE_gpencil_modifier_dependsOnTime(GpencilModifierData *md) +{ + const GpencilModifierTypeInfo *mti = BKE_gpencil_modifierType_getInfo(md->type); + + return mti->dependsOnTime && mti->dependsOnTime(md); +} + +const GpencilModifierTypeInfo *BKE_gpencil_modifierType_getInfo(GpencilModifierType type) +{ + /* type unsigned, no need to check < 0 */ + if (type < NUM_GREASEPENCIL_MODIFIER_TYPES && modifier_gpencil_types[type]->name[0] != '\0') { + return modifier_gpencil_types[type]; + } + else { + return NULL; + } +} + +void BKE_gpencil_modifier_copyData_generic(const GpencilModifierData *md_src, GpencilModifierData *md_dst) +{ + const GpencilModifierTypeInfo *mti = BKE_gpencil_modifierType_getInfo(md_src->type); + + /* md_dst may have alredy be fully initialized with some extra allocated data, + * we need to free it now to avoid memleak. */ + if (mti->freeData) { + mti->freeData(md_dst); + } + + const size_t data_size = sizeof(GpencilModifierData); + const char *md_src_data = ((const char *)md_src) + data_size; + char *md_dst_data = ((char *)md_dst) + data_size; + BLI_assert(data_size <= (size_t)mti->struct_size); + memcpy(md_dst_data, md_src_data, (size_t)mti->struct_size - data_size); +} + +static void gpencil_modifier_copy_data_id_us_cb(void *UNUSED(userData), Object *UNUSED(ob), ID **idpoin, int cb_flag) +{ + ID *id = *idpoin; + if (id != NULL && (cb_flag & IDWALK_CB_USER) != 0) { + id_us_plus(id); + } +} + +void BKE_gpencil_modifier_copyData_ex(GpencilModifierData *md, GpencilModifierData *target, const int flag) +{ + const GpencilModifierTypeInfo *mti = BKE_gpencil_modifierType_getInfo(md->type); + + target->mode = md->mode; + target->flag = md->flag; + + if (mti->copyData) { + mti->copyData(md, target); + } + + if ((flag & LIB_ID_CREATE_NO_USER_REFCOUNT) == 0) { + if (mti->foreachIDLink) { + mti->foreachIDLink(target, NULL, gpencil_modifier_copy_data_id_us_cb, NULL); + } + else if (mti->foreachObjectLink) { + mti->foreachObjectLink(target, NULL, (GreasePencilObjectWalkFunc)gpencil_modifier_copy_data_id_us_cb, NULL); + } + } +} + +void BKE_gpencil_modifier_copyData(GpencilModifierData *md, GpencilModifierData *target) +{ + BKE_gpencil_modifier_copyData_ex(md, target, 0); +} + +GpencilModifierData *BKE_gpencil_modifiers_findByType(Object *ob, GpencilModifierType type) +{ + GpencilModifierData *md = ob->greasepencil_modifiers.first; + + for (; md; md = md->next) + if (md->type == type) + break; + + return md; +} + +void BKE_gpencil_modifiers_foreachIDLink(Object *ob, GreasePencilIDWalkFunc walk, void *userData) +{ + GpencilModifierData *md = ob->greasepencil_modifiers.first; + + for (; md; md = md->next) { + const GpencilModifierTypeInfo *mti = BKE_gpencil_modifierType_getInfo(md->type); + + if (mti->foreachIDLink) mti->foreachIDLink(md, ob, walk, userData); + else if (mti->foreachObjectLink) { + /* each Object can masquerade as an ID, so this should be OK */ + GreasePencilObjectWalkFunc fp = (GreasePencilObjectWalkFunc)walk; + mti->foreachObjectLink(md, ob, fp, userData); + } + } +} + +void BKE_gpencil_modifiers_foreachTexLink(Object *ob, GreasePencilTexWalkFunc walk, void *userData) +{ + GpencilModifierData *md = ob->greasepencil_modifiers.first; + + for (; md; md = md->next) { + const GpencilModifierTypeInfo *mti = BKE_gpencil_modifierType_getInfo(md->type); + + if (mti->foreachTexLink) + mti->foreachTexLink(md, ob, walk, userData); + } +} + +GpencilModifierData *BKE_gpencil_modifiers_findByName(Object *ob, const char *name) +{ + return BLI_findstring(&(ob->greasepencil_modifiers), name, offsetof(GpencilModifierData, name)); +} + +/* helper function for per-instance positioning */ +void BKE_gpencil_instance_modifier_instance_tfm(InstanceGpencilModifierData *mmd, const int elem_idx[3], float r_mat[4][4]) +{ + float offset[3], rot[3], scale[3]; + int ri = mmd->rnd[0]; + float factor; + + offset[0] = mmd->offset[0] * elem_idx[0]; + offset[1] = mmd->offset[1] * elem_idx[1]; + offset[2] = mmd->offset[2] * elem_idx[2]; + + /* rotation */ + if (mmd->flag & GP_INSTANCE_RANDOM_ROT) { + factor = mmd->rnd_rot * mmd->rnd[ri]; + mul_v3_v3fl(rot, mmd->rot, factor); + add_v3_v3(rot, mmd->rot); + } + else { + copy_v3_v3(rot, mmd->rot); + } + + /* scale */ + if (mmd->flag & GP_INSTANCE_RANDOM_SIZE) { + factor = mmd->rnd_size * mmd->rnd[ri]; + mul_v3_v3fl(scale, mmd->scale, factor); + add_v3_v3(scale, mmd->scale); + } + else { + copy_v3_v3(scale, mmd->scale); + } + + /* advance random index */ + mmd->rnd[0]++; + if (mmd->rnd[0] > 19) { + mmd->rnd[0] = 1; + } + + /* calculate matrix */ + loc_eul_size_to_mat4(r_mat, offset, rot, scale); +} diff --git a/source/blender/blenkernel/intern/icons.c b/source/blender/blenkernel/intern/icons.c index 1c2575dfa52..3a4bf53e22d 100644 --- a/source/blender/blenkernel/intern/icons.c +++ b/source/blender/blenkernel/intern/icons.c @@ -37,6 +37,8 @@ #include "MEM_guardedalloc.h" +#include "DNA_brush_types.h" +#include "DNA_gpencil_types.h" #include "DNA_group_types.h" #include "DNA_lamp_types.h" #include "DNA_material_types.h" @@ -45,7 +47,6 @@ #include "DNA_screen_types.h" #include "DNA_texture_types.h" #include "DNA_world_types.h" -#include "DNA_brush_types.h" #include "BLI_utildefines.h" #include "BLI_ghash.h" @@ -127,6 +128,9 @@ static void icon_free_data(int icon_id, Icon *icon) else if (icon->obj_type == ICON_DATA_PREVIEW) { ((PreviewImage *)(icon->obj))->icon_id = 0; } + else if (icon->obj_type == ICON_DATA_GPLAYER) { + ((bGPDlayer *)(icon->obj))->runtime.icon_id = 0; + } else if (icon->obj_type == ICON_DATA_GEOM) { ((struct Icon_Geom *)(icon->obj))->icon_id = 0; } @@ -598,6 +602,44 @@ int BKE_icon_id_ensure(struct ID *id) return icon_id_ensure_create_icon(id); } + +static int icon_gplayer_color_ensure_create_icon(bGPDlayer *gpl) +{ + BLI_assert(BLI_thread_is_main()); + + /* NOTE: The color previews for GP Layers don't really need + * to be "rendered" to image per se (as it will just be a plain + * colored rectangle), we need to define icon data here so that + * we can store a pointer to the layer data in icon->obj. + */ + Icon *icon = icon_create(gpl->runtime.icon_id, ICON_DATA_GPLAYER, gpl); + icon->flag = ICON_FLAG_MANAGED; + + return gpl->runtime.icon_id; +} + +int BKE_icon_gplayer_color_ensure(bGPDlayer *gpl) +{ + /* Never handle icons in non-main thread! */ + BLI_assert(BLI_thread_is_main()); + + if (!gpl || G.background) { + return 0; + } + + if (gpl->runtime.icon_id) + return gpl->runtime.icon_id; + + gpl->runtime.icon_id = get_next_free_id(); + + if (!gpl->runtime.icon_id) { + printf("%s: Internal error - not enough IDs\n", __func__); + return 0; + } + + return icon_gplayer_color_ensure_create_icon(gpl); +} + /** * Return icon id of given preview, or create new icon if not found. */ diff --git a/source/blender/blenkernel/intern/ipo.c b/source/blender/blenkernel/intern/ipo.c index b7ca73e9f4a..3be40b414a6 100644 --- a/source/blender/blenkernel/intern/ipo.c +++ b/source/blender/blenkernel/intern/ipo.c @@ -1717,8 +1717,7 @@ void do_versions_ipos_to_animato(Main *bmain) /* IPO first to take into any non-NLA'd Object Animation */ if (ob->ipo) { ipo_to_animdata(bmain, id, ob->ipo, NULL, NULL, NULL); - - id_us_min(&ob->ipo->id); + /* No need to id_us_min ipo ID here, ipo_to_animdata already does it. */ ob->ipo = NULL; } @@ -1751,7 +1750,7 @@ void do_versions_ipos_to_animato(Main *bmain) /* IPO second... */ if (ob->ipo) { ipo_to_animdata(bmain, id, ob->ipo, NULL, NULL, NULL); - id_us_min(&ob->ipo->id); + /* No need to id_us_min ipo ID here, ipo_to_animdata already does it. */ ob->ipo = NULL; } } diff --git a/source/blender/blenkernel/intern/key.c b/source/blender/blenkernel/intern/key.c index 13f7716cd80..0d8e281fa06 100644 --- a/source/blender/blenkernel/intern/key.c +++ b/source/blender/blenkernel/intern/key.c @@ -1457,7 +1457,7 @@ KeyBlock *BKE_keyblock_add(Key *key, const char *name) kb = MEM_callocN(sizeof(KeyBlock), "Keyblock"); BLI_addtail(&key->block, kb); - kb->type = KEY_CARDINAL; + kb->type = KEY_LINEAR; tot = BLI_listbase_count(&key->block); if (name) { diff --git a/source/blender/blenkernel/intern/lamp.c b/source/blender/blenkernel/intern/lamp.c index 1d5b6de22f4..2c1b36d3496 100644 --- a/source/blender/blenkernel/intern/lamp.c +++ b/source/blender/blenkernel/intern/lamp.c @@ -84,10 +84,10 @@ void BKE_lamp_init(Lamp *la) la->cascade_count = 4; la->cascade_exponent = 0.8f; la->cascade_fade = 0.1f; - la->contact_dist = 1.0f; + la->contact_dist = 0.2f; la->contact_bias = 0.03f; la->contact_spread = 0.2f; - la->contact_thickness = 0.5f; + la->contact_thickness = 0.2f; la->spec_fac = 1.0f; curvemapping_initialize(la->curfalloff); diff --git a/source/blender/blenkernel/intern/lattice.c b/source/blender/blenkernel/intern/lattice.c index 78ff43c2981..04830eb1081 100644 --- a/source/blender/blenkernel/intern/lattice.c +++ b/source/blender/blenkernel/intern/lattice.c @@ -211,9 +211,9 @@ void BKE_lattice_resize(Lattice *lt, int uNew, int vNew, int wNew, Object *ltOb) /* works best if we force to linear type (endpoints match) */ lt->typeu = lt->typev = lt->typew = KEY_LINEAR; - if (ltOb->curve_cache) { + if (ltOb->runtime.curve_cache) { /* prevent using deformed locations */ - BKE_displist_free(<Ob->curve_cache->disp); + BKE_displist_free(<Ob->runtime.curve_cache->disp); } copy_m4_m4(mat, ltOb->obmat); @@ -349,7 +349,7 @@ LatticeDeformData *init_latt_deform(Object *oblatt, Object *ob) /* we make an array with all differences */ Lattice *lt = oblatt->data; BPoint *bp; - DispList *dl = oblatt->curve_cache ? BKE_displist_find(&oblatt->curve_cache->disp, DL_VERTS) : NULL; + DispList *dl = oblatt->runtime.curve_cache ? BKE_displist_find(&oblatt->runtime.curve_cache->disp, DL_VERTS) : NULL; const float *co = dl ? dl->verts : NULL; float *fp, imat[4][4]; float fu, fv, fw; @@ -558,7 +558,7 @@ static bool where_on_path_deform(Object *ob, float ctime, float vec[4], float di int cycl = 0; /* test for cyclic */ - bl = ob->curve_cache->bev.first; + bl = ob->runtime.curve_cache->bev.first; if (!bl->nr) return false; if (bl->poly > -1) cycl = 1; @@ -573,7 +573,7 @@ static bool where_on_path_deform(Object *ob, float ctime, float vec[4], float di if (where_on_path(ob, ctime1, vec, dir, quat, radius, NULL)) { if (cycl == 0) { - Path *path = ob->curve_cache->path; + Path *path = ob->runtime.curve_cache->path; float dvec[3]; if (ctime < 0.0f) { @@ -610,12 +610,12 @@ static bool calc_curve_deform(Object *par, float co[3], short index; const bool is_neg_axis = (axis > 2); - if (par->curve_cache == NULL) { + if (par->runtime.curve_cache == NULL) { /* Happens with a cyclic dependencies. */ return false; } - if (par->curve_cache->path == NULL) { + if (par->runtime.curve_cache->path == NULL) { return false; /* happens on append, cyclic dependencies and empty curves */ } @@ -625,7 +625,7 @@ static bool calc_curve_deform(Object *par, float co[3], if (cu->flag & CU_STRETCH) fac = (-co[index] - cd->dmax[index]) / (cd->dmax[index] - cd->dmin[index]); else - fac = -(co[index] - cd->dmax[index]) / (par->curve_cache->path->totdist); + fac = -(co[index] - cd->dmax[index]) / (par->runtime.curve_cache->path->totdist); } else { index = axis; @@ -633,8 +633,8 @@ static bool calc_curve_deform(Object *par, float co[3], fac = (co[index] - cd->dmin[index]) / (cd->dmax[index] - cd->dmin[index]); } else { - if (LIKELY(par->curve_cache->path->totdist > FLT_EPSILON)) { - fac = +(co[index] - cd->dmin[index]) / (par->curve_cache->path->totdist); + if (LIKELY(par->runtime.curve_cache->path->totdist > FLT_EPSILON)) { + fac = +(co[index] - cd->dmin[index]) / (par->runtime.curve_cache->path->totdist); } else { fac = 0.0f; @@ -1035,11 +1035,11 @@ void BKE_lattice_modifiers_calc(struct Depsgraph *depsgraph, Scene *scene, Objec int numVerts, editmode = (lt->editlatt != NULL); const ModifierEvalContext mectx = {depsgraph, ob, 0}; - if (ob->curve_cache) { - BKE_displist_free(&ob->curve_cache->disp); + if (ob->runtime.curve_cache) { + BKE_displist_free(&ob->runtime.curve_cache->disp); } else { - ob->curve_cache = MEM_callocN(sizeof(CurveCache), "CurveCache for lattice"); + ob->runtime.curve_cache = MEM_callocN(sizeof(CurveCache), "CurveCache for lattice"); } for (; md; md = md->next) { @@ -1071,7 +1071,7 @@ void BKE_lattice_modifiers_calc(struct Depsgraph *depsgraph, Scene *scene, Objec dl->nr = numVerts; dl->verts = (float *) vertexCos; - BLI_addtail(&ob->curve_cache->disp, dl); + BLI_addtail(&ob->runtime.curve_cache->disp, dl); } } @@ -1145,7 +1145,7 @@ BoundBox *BKE_lattice_boundbox_get(Object *ob) void BKE_lattice_minmax_dl(Object *ob, Lattice *lt, float min[3], float max[3]) { - DispList *dl = ob->curve_cache ? BKE_displist_find(&ob->curve_cache->disp, DL_VERTS) : NULL; + DispList *dl = ob->runtime.curve_cache ? BKE_displist_find(&ob->runtime.curve_cache->disp, DL_VERTS) : NULL; if (!dl) { BKE_lattice_minmax(lt, min, max); diff --git a/source/blender/blenkernel/intern/layer.c b/source/blender/blenkernel/intern/layer.c index 2f5c8e7817e..1396ad1f97c 100644 --- a/source/blender/blenkernel/intern/layer.c +++ b/source/blender/blenkernel/intern/layer.c @@ -715,6 +715,14 @@ static int layer_collection_sync( lc->runtime_flag |= LAYER_COLLECTION_HAS_VISIBLE_OBJECTS; } + /* Holdout and indirect only */ + if (lc->flag & LAYER_COLLECTION_HOLDOUT) { + base->flag |= BASE_HOLDOUT; + } + if (lc->flag & LAYER_COLLECTION_INDIRECT_ONLY) { + base->flag |= BASE_INDIRECT_ONLY; + } + lc->runtime_flag |= LAYER_COLLECTION_HAS_OBJECTS; } @@ -750,7 +758,13 @@ void BKE_layer_collection_sync(const Scene *scene, ViewLayer *view_layer) /* Clear visible and selectable flags to be reset. */ for (Base *base = view_layer->object_bases.first; base; base = base->next) { - base->flag &= ~(BASE_VISIBLE | BASE_ENABLED | BASE_SELECTABLE | BASE_ENABLED_VIEWPORT | BASE_ENABLED_RENDER); + base->flag &= ~(BASE_VISIBLE | + BASE_ENABLED | + BASE_SELECTABLE | + BASE_ENABLED_VIEWPORT | + BASE_ENABLED_RENDER | + BASE_HOLDOUT | + BASE_INDIRECT_ONLY); } view_layer->runtime_flag = 0; diff --git a/source/blender/blenkernel/intern/library.c b/source/blender/blenkernel/intern/library.c index 5b058608e40..c7d06f4fba4 100644 --- a/source/blender/blenkernel/intern/library.c +++ b/source/blender/blenkernel/intern/library.c @@ -638,7 +638,7 @@ bool BKE_id_copy_ex(Main *bmain, const ID *id, ID **r_newid, const int flag, con BKE_particlesettings_copy_data(bmain, (ParticleSettings *)*r_newid, (ParticleSettings *)id, flag); break; case ID_GD: - BKE_gpencil_copy_data(bmain, (bGPdata *)*r_newid, (bGPdata *)id, flag); + BKE_gpencil_copy_data((bGPdata *)*r_newid, (bGPdata *)id, flag); break; case ID_MC: BKE_movieclip_copy_data(bmain, (MovieClip *)*r_newid, (MovieClip *)id, flag); @@ -1086,6 +1086,7 @@ int set_listbasepointers(Main *main, ListBase **lb) lb[INDEX_ID_IP] = &(main->ipo); lb[INDEX_ID_AC] = &(main->action); /* moved here to avoid problems when freeing with animato (aligorith) */ lb[INDEX_ID_KE] = &(main->key); + lb[INDEX_ID_PAL] = &(main->palettes); /* referenced by gpencil, so needs to be before that to avoid crashes */ lb[INDEX_ID_GD] = &(main->gpencil); /* referenced by nodes, objects, view, scene etc, before to free after. */ lb[INDEX_ID_NT] = &(main->nodetree); lb[INDEX_ID_IM] = &(main->image); @@ -1421,13 +1422,13 @@ void *BKE_id_new_nomain(const short type, const char *name) /* by spec, animdata is first item after ID */ /* and, trust that BKE_animdata_from_id() will only find AnimData for valid ID-types */ -static void id_copy_animdata(Main *bmain, ID *id, const bool do_action) +static void id_copy_animdata(Main *bmain, ID *id, const bool do_action, const bool do_id_user) { AnimData *adt = BKE_animdata_from_id(id); if (adt) { IdAdtTemplate *iat = (IdAdtTemplate *)id; - iat->adt = BKE_animdata_copy(bmain, iat->adt, do_action, true); /* could be set to false, need to investigate */ + iat->adt = BKE_animdata_copy(bmain, iat->adt, do_action, do_id_user); } } @@ -1484,7 +1485,9 @@ void BKE_libblock_copy_ex(Main *bmain, const ID *id, ID **r_newid, const int fla /* the duplicate should get a copy of the animdata */ if ((flag & LIB_ID_COPY_NO_ANIMDATA) == 0) { BLI_assert((flag & LIB_ID_COPY_ACTIONS) == 0 || (flag & LIB_ID_CREATE_NO_MAIN) == 0); - id_copy_animdata(bmain, new_id, (flag & LIB_ID_COPY_ACTIONS) != 0 && (flag & LIB_ID_CREATE_NO_MAIN) == 0); + id_copy_animdata(bmain, new_id, + (flag & LIB_ID_COPY_ACTIONS) != 0 && (flag & LIB_ID_CREATE_NO_MAIN) == 0, + (flag & LIB_ID_CREATE_NO_USER_REFCOUNT) == 0); } else if (id_can_have_animdata(new_id)) { IdAdtTemplate *iat = (IdAdtTemplate *)new_id; @@ -2479,7 +2482,7 @@ void BKE_library_make_local( * Try "make all local" in 04_01_H.lighting.blend from Agent327 without this, e.g. */ for (Object *ob = bmain->object.first; ob; ob = ob->id.next) { if (ob->data != NULL && ob->type == OB_ARMATURE && ob->pose != NULL && ob->pose->flag & POSE_RECALC) { - BKE_pose_rebuild(ob, ob->data); + BKE_pose_rebuild(bmain, ob, ob->data, true); } } diff --git a/source/blender/blenkernel/intern/library_query.c b/source/blender/blenkernel/intern/library_query.c index 656d3cff307..b2f63f246ba 100644 --- a/source/blender/blenkernel/intern/library_query.c +++ b/source/blender/blenkernel/intern/library_query.c @@ -418,7 +418,6 @@ void BKE_library_foreach_ID_link(Main *bmain, ID *id, LibraryIDLinkCallback call SEQ_END } - CALLBACK_INVOKE(scene->gpd, IDWALK_CB_USER); for (CollectionObject *cob = scene->master_collection->gobject.first; cob; cob = cob->next) { CALLBACK_INVOKE(cob->ob, IDWALK_CB_USER); @@ -479,6 +478,9 @@ void BKE_library_foreach_ID_link(Main *bmain, ID *id, LibraryIDLinkCallback call if (toolsett->uvsculpt) { library_foreach_paint(&data, &toolsett->uvsculpt->paint); } + if (toolsett->gp_paint) { + library_foreach_paint(&data, &toolsett->gp_paint->paint); + } } if (scene->rigidbody_world) { @@ -652,6 +654,10 @@ void BKE_library_foreach_ID_link(Main *bmain, ID *id, LibraryIDLinkCallback call if (material->texpaintslot != NULL) { CALLBACK_INVOKE(material->texpaintslot->ima, IDWALK_CB_NOP); } + if (material->gp_style != NULL) { + CALLBACK_INVOKE(material->gp_style->sima, IDWALK_CB_USER); + CALLBACK_INVOKE(material->gp_style->ima, IDWALK_CB_USER); + } break; } @@ -687,6 +693,15 @@ void BKE_library_foreach_ID_link(Main *bmain, ID *id, LibraryIDLinkCallback call { Camera *camera = (Camera *) id; CALLBACK_INVOKE(camera->dof_ob, IDWALK_CB_NOP); + for (CameraBGImage *bgpic = camera->bg_images.first; bgpic; bgpic = bgpic->next) { + if (bgpic->source == CAM_BGIMG_SOURCE_IMAGE) { + CALLBACK_INVOKE(bgpic->ima, IDWALK_CB_USER); + } + else if (bgpic->source == CAM_BGIMG_SOURCE_MOVIE) { + CALLBACK_INVOKE(bgpic->clip, IDWALK_CB_USER); + } + } + break; } @@ -769,6 +784,9 @@ void BKE_library_foreach_ID_link(Main *bmain, ID *id, LibraryIDLinkCallback call CALLBACK_INVOKE(brush->toggle_brush, IDWALK_CB_NOP); CALLBACK_INVOKE(brush->clone.image, IDWALK_CB_NOP); CALLBACK_INVOKE(brush->paint_curve, IDWALK_CB_USER); + if (brush->gpencil_settings) { + CALLBACK_INVOKE(brush->gpencil_settings->material, IDWALK_CB_USER); + } library_foreach_mtex(&data, &brush->mtex); library_foreach_mtex(&data, &brush->mask_mtex); break; @@ -952,10 +970,15 @@ void BKE_library_foreach_ID_link(Main *bmain, ID *id, LibraryIDLinkCallback call case ID_GD: { bGPdata *gpencil = (bGPdata *) id; + /* materials */ + for (i = 0; i < gpencil->totcol; i++) { + CALLBACK_INVOKE(gpencil->mat[i], IDWALK_CB_USER); + } - for (bGPDlayer *gp_layer = gpencil->layers.first; gp_layer; gp_layer = gp_layer->next) { - CALLBACK_INVOKE(gp_layer->parent, IDWALK_CB_NOP); + for (bGPDlayer *gplayer = gpencil->layers.first; gplayer != NULL; gplayer = gplayer->next) { + CALLBACK_INVOKE(gplayer->parent, IDWALK_CB_NOP); } + break; } @@ -1083,7 +1106,7 @@ bool BKE_library_id_can_use_idtype(ID *id_owner, const short id_type_used) return true; #endif case ID_BR: - return ELEM(id_type_used, ID_BR, ID_IM, ID_PC, ID_TE); + return ELEM(id_type_used, ID_BR, ID_IM, ID_PC, ID_TE, ID_MA); case ID_PA: return ELEM(id_type_used, ID_OB, ID_GR, ID_TE); case ID_MC: @@ -1094,6 +1117,8 @@ bool BKE_library_id_can_use_idtype(ID *id_owner, const short id_type_used) return (ELEM(id_type_used, ID_TE, ID_OB)); case ID_LP: return ELEM(id_type_used, ID_IM); + case ID_GD: + return ELEM(id_type_used, ID_MA); case ID_GM: return true; case ID_WS: @@ -1104,7 +1129,6 @@ bool BKE_library_id_can_use_idtype(ID *id_owner, const short id_type_used) case ID_SO: case ID_AR: case ID_AC: - case ID_GD: case ID_WM: case ID_PAL: case ID_PC: diff --git a/source/blender/blenkernel/intern/lightprobe.c b/source/blender/blenkernel/intern/lightprobe.c index 057b6aaaf65..baf0cdbe62f 100644 --- a/source/blender/blenkernel/intern/lightprobe.c +++ b/source/blender/blenkernel/intern/lightprobe.c @@ -53,7 +53,6 @@ void BKE_lightprobe_init(LightProbe *probe) probe->vis_bias = 1.0f; probe->vis_blur = 0.2f; probe->intensity = 1.0f; - probe->data_draw_size = 1.0f; probe->flag = LIGHTPROBE_FLAG_SHOW_INFLUENCE | LIGHTPROBE_FLAG_SHOW_DATA; } diff --git a/source/blender/blenkernel/intern/material.c b/source/blender/blenkernel/intern/material.c index 4bdb59b1374..0b27e472f4a 100644 --- a/source/blender/blenkernel/intern/material.c +++ b/source/blender/blenkernel/intern/material.c @@ -44,6 +44,7 @@ #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" #include "DNA_customdata_types.h" +#include "DNA_gpencil_types.h" #include "DNA_ID.h" #include "DNA_meta_types.h" #include "DNA_node_types.h" @@ -59,6 +60,7 @@ #include "BKE_animsys.h" #include "BKE_displist.h" #include "BKE_global.h" +#include "BKE_gpencil.h" #include "BKE_icons.h" #include "BKE_image.h" #include "BKE_library.h" @@ -104,10 +106,30 @@ void BKE_material_free(Material *ma) MEM_SAFE_FREE(ma->texpaintslot); + MEM_SAFE_FREE(ma->gp_style); + BKE_icon_id_delete((ID *)ma); BKE_previewimg_free(&ma->preview); } +void BKE_material_init_gpencil_settings(Material *ma) +{ + if ((ma) && (ma->gp_style == NULL)) { + ma->gp_style = MEM_callocN(sizeof(MaterialGPencilStyle), "Grease Pencil Material Settings"); + + MaterialGPencilStyle *gp_style = ma->gp_style; + /* set basic settings */ + gp_style->stroke_rgba[3] = 1.0f; + gp_style->pattern_gridsize = 0.1f; + gp_style->gradient_radius = 0.5f; + ARRAY_SET_ITEMS(gp_style->mix_rgba, 1.0f, 1.0f, 1.0f, 0.2f); + ARRAY_SET_ITEMS(gp_style->gradient_scale, 1.0f, 1.0f); + ARRAY_SET_ITEMS(gp_style->texture_scale, 1.0f, 1.0f); + gp_style->texture_opacity = 1.0f; + gp_style->texture_pixsize = 100.0f; + } +} + void BKE_material_init(Material *ma) { BLI_assert(MEMCMP_STRUCT_OFS_IS_ZERO(ma, id)); @@ -125,6 +147,7 @@ void BKE_material_init(Material *ma) ma->preview = NULL; ma->alpha_threshold = 0.5f; + } Material *BKE_material_add(Main *bmain, const char *name) @@ -138,6 +161,19 @@ Material *BKE_material_add(Main *bmain, const char *name) return ma; } +Material *BKE_material_add_gpencil(Main *bmain, const char *name) +{ + Material *ma; + + ma = BKE_material_add(bmain, name); + + /* grease pencil settings */ + BKE_material_init_gpencil_settings(ma); + + return ma; +} + + /** * Only copy internal data of Material ID from source to already allocated/initialized destination. * You probably nerver want to use that directly, use id_copy or BKE_id_copy_ex for typical needs. @@ -165,6 +201,10 @@ void BKE_material_copy_data(Main *bmain, Material *ma_dst, const Material *ma_sr ma_dst->texpaintslot = MEM_dupallocN(ma_src->texpaintslot); } + if (ma_src->gp_style != NULL) { + ma_dst->gp_style = MEM_dupallocN(ma_src->gp_style); + } + BLI_listbase_clear(&ma_dst->gpumaterial); /* TODO Duplicate Engine Settings and set runtime to NULL */ @@ -200,6 +240,7 @@ Material *BKE_material_localize(Material *ma) man->texpaintslot = NULL; man->preview = NULL; + /* man->gp_style = NULL; */ /* XXX: We probably don't want to clear here, or else we may get problems with COW later? */ BLI_listbase_clear(&man->gpumaterial); /* TODO Duplicate Engine Settings and set runtime to NULL */ @@ -219,6 +260,7 @@ Material ***give_matarar(Object *ob) Mesh *me; Curve *cu; MetaBall *mb; + bGPdata *gpd; if (ob->type == OB_MESH) { me = ob->data; @@ -232,6 +274,10 @@ Material ***give_matarar(Object *ob) mb = ob->data; return &(mb->mat); } + else if (ob->type == OB_GPENCIL) { + gpd = ob->data; + return &(gpd->mat); + } else if (ob->type == OB_GROOM) { Groom *groom = ob->data; return &(groom->mat); @@ -244,6 +290,7 @@ short *give_totcolp(Object *ob) Mesh *me; Curve *cu; MetaBall *mb; + bGPdata *gpd; if (ob->type == OB_MESH) { me = ob->data; @@ -257,6 +304,10 @@ short *give_totcolp(Object *ob) mb = ob->data; return &(mb->totcol); } + else if (ob->type == OB_GPENCIL) { + gpd = ob->data; + return &(gpd->totcol); + } else if (ob->type == OB_GROOM) { Groom *groom = ob->data; return &(groom->totcol); @@ -277,6 +328,8 @@ Material ***give_matarar_id(ID *id) return &(((Curve *)id)->mat); case ID_MB: return &(((MetaBall *)id)->mat); + case ID_GD: + return &(((bGPdata *)id)->mat); case ID_GM: return &(((Groom *)id)->mat); default: @@ -297,6 +350,8 @@ short *give_totcolp_id(ID *id) return &(((Curve *)id)->totcol); case ID_MB: return &(((MetaBall *)id)->totcol); + case ID_GD: + return &(((bGPdata *)id)->totcol); case ID_GM: return &(((Groom *)id)->totcol); default: @@ -320,6 +375,9 @@ static void material_data_index_remove_id(ID *id, short index) case ID_MB: /* meta-elems don't have materials atm */ break; + case ID_GD: + BKE_gpencil_material_index_remove((bGPdata *)id, index); + break; default: break; } @@ -500,6 +558,21 @@ Material *give_current_material(Object *ob, short act) return ma; } +MaterialGPencilStyle *BKE_material_gpencil_settings_get(Object *ob, short act) +{ + Material *ma = give_current_material(ob, act); + if (ma != NULL) { + if (ma->gp_style == NULL) { + BKE_material_init_gpencil_settings(ma); + } + + return ma->gp_style; + } + else { + return NULL; + } +} + Material *give_node_material(Material *ma) { if (ma && ma->use_nodes && ma->nodetree) { @@ -740,6 +813,9 @@ void BKE_material_remap_object(Object *ob, const unsigned int *remap) else if (ELEM(ob->type, OB_CURVE, OB_SURF, OB_FONT)) { BKE_curve_material_remap(ob->data, remap, ob->totcol); } + if (ob->type == OB_GPENCIL) { + BKE_gpencil_material_remap(ob->data, remap, ob->totcol); + } else { /* add support for this object data! */ BLI_assert(matar == NULL); @@ -937,10 +1013,10 @@ bool BKE_object_material_slot_remove(Main *bmain, Object *ob) } /* check indices from mesh */ - if (ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT)) { + if (ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_GPENCIL)) { material_data_index_remove_id((ID *)ob->data, actcol - 1); - if (ob->curve_cache) { - BKE_displist_free(&ob->curve_cache->disp); + if (ob->runtime.curve_cache) { + BKE_displist_free(&ob->runtime.curve_cache->disp); } } diff --git a/source/blender/blenkernel/intern/mball.c b/source/blender/blenkernel/intern/mball.c index 2ff69c5ee6d..43b7eba2810 100644 --- a/source/blender/blenkernel/intern/mball.c +++ b/source/blender/blenkernel/intern/mball.c @@ -207,7 +207,7 @@ void BKE_mball_texspace_calc(Object *ob) (min)[0] = (min)[1] = (min)[2] = 1.0e30f; (max)[0] = (max)[1] = (max)[2] = -1.0e30f; - dl = ob->curve_cache->disp.first; + dl = ob->runtime.curve_cache->disp.first; while (dl) { tot = dl->nr; if (tot) do_it = true; diff --git a/source/blender/blenkernel/intern/mesh_convert.c b/source/blender/blenkernel/intern/mesh_convert.c index 738f116310b..2e2afa6834b 100644 --- a/source/blender/blenkernel/intern/mesh_convert.c +++ b/source/blender/blenkernel/intern/mesh_convert.c @@ -217,8 +217,8 @@ int BKE_mesh_nurbs_to_mdata( { ListBase disp = {NULL, NULL}; - if (ob->curve_cache) { - disp = ob->curve_cache->disp; + if (ob->runtime.curve_cache) { + disp = ob->runtime.curve_cache->disp; } return BKE_mesh_nurbs_displist_to_mdata( @@ -537,8 +537,8 @@ Mesh *BKE_mesh_new_nomain_from_curve(Object *ob) { ListBase disp = {NULL, NULL}; - if (ob->curve_cache) { - disp = ob->curve_cache->disp; + if (ob->runtime.curve_cache) { + disp = ob->runtime.curve_cache->disp; } return BKE_mesh_new_nomain_from_curve_displist(ob, &disp); @@ -650,8 +650,8 @@ void BKE_mesh_from_nurbs(Main *bmain, Object *ob) bool use_orco_uv = (cu->flag & CU_UV_ORCO) != 0; ListBase disp = {NULL, NULL}; - if (ob->curve_cache) { - disp = ob->curve_cache->disp; + if (ob->runtime.curve_cache) { + disp = ob->runtime.curve_cache->disp; } BKE_mesh_from_nurbs_displist(bmain, ob, &disp, use_orco_uv, cu->id.name, false); @@ -871,11 +871,11 @@ Mesh *BKE_mesh_new_from_object( * * TODO(sergey): Look into more proper solution. */ - if (ob->curve_cache != NULL) { - if (tmpobj->curve_cache == NULL) { - tmpobj->curve_cache = MEM_callocN(sizeof(CurveCache), "CurveCache for curve types"); + if (ob->runtime.curve_cache != NULL) { + if (tmpobj->runtime.curve_cache == NULL) { + tmpobj->runtime.curve_cache = MEM_callocN(sizeof(CurveCache), "CurveCache for curve types"); } - BKE_displist_copy(&tmpobj->curve_cache->disp, &ob->curve_cache->disp); + BKE_displist_copy(&tmpobj->runtime.curve_cache->disp, &ob->runtime.curve_cache->disp); } /* if getting the original caged mesh, delete object modifiers */ @@ -953,8 +953,8 @@ Mesh *BKE_mesh_new_from_object( } else { ListBase disp = {NULL, NULL}; - if (ob->curve_cache) { - disp = ob->curve_cache->disp; + if (ob->runtime.curve_cache) { + disp = ob->runtime.curve_cache->disp; } BKE_mesh_from_metaball(&disp, tmpmesh); } diff --git a/source/blender/blenkernel/intern/mesh_evaluate.c b/source/blender/blenkernel/intern/mesh_evaluate.c index ac935bb7f81..66419b03e01 100644 --- a/source/blender/blenkernel/intern/mesh_evaluate.c +++ b/source/blender/blenkernel/intern/mesh_evaluate.c @@ -480,6 +480,8 @@ void BKE_lnor_spacearr_init(MLoopNorSpaceArray *lnors_spacearr, const int numLoo mem = lnors_spacearr->mem; lnors_spacearr->lspacearr = BLI_memarena_calloc(mem, sizeof(MLoopNorSpace *) * (size_t)numLoops); lnors_spacearr->loops_pool = BLI_memarena_alloc(mem, sizeof(LinkNode) * (size_t)numLoops); + + lnors_spacearr->num_spaces = 0; } BLI_assert(ELEM(data_type, MLNOR_SPACEARR_BMLOOP_PTR, MLNOR_SPACEARR_LOOP_INDEX)); lnors_spacearr->data_type = data_type; @@ -487,21 +489,24 @@ void BKE_lnor_spacearr_init(MLoopNorSpaceArray *lnors_spacearr, const int numLoo void BKE_lnor_spacearr_clear(MLoopNorSpaceArray *lnors_spacearr) { - BLI_memarena_clear(lnors_spacearr->mem); + lnors_spacearr->num_spaces = 0; lnors_spacearr->lspacearr = NULL; lnors_spacearr->loops_pool = NULL; + BLI_memarena_clear(lnors_spacearr->mem); } void BKE_lnor_spacearr_free(MLoopNorSpaceArray *lnors_spacearr) { - BLI_memarena_free(lnors_spacearr->mem); + lnors_spacearr->num_spaces = 0; lnors_spacearr->lspacearr = NULL; lnors_spacearr->loops_pool = NULL; + BLI_memarena_free(lnors_spacearr->mem); lnors_spacearr->mem = NULL; } MLoopNorSpace *BKE_lnor_space_create(MLoopNorSpaceArray *lnors_spacearr) { + lnors_spacearr->num_spaces++; return BLI_memarena_calloc(lnors_spacearr->mem, sizeof(MLoopNorSpace)); } diff --git a/source/blender/blenkernel/intern/mesh_mapping.c b/source/blender/blenkernel/intern/mesh_mapping.c index 699d6bce2b0..5c9849f6b74 100644 --- a/source/blender/blenkernel/intern/mesh_mapping.c +++ b/source/blender/blenkernel/intern/mesh_mapping.c @@ -55,13 +55,13 @@ * but for now this replaces it because its unused. */ UvVertMap *BKE_mesh_uv_vert_map_create( - struct MPoly *mpoly, struct MLoop *mloop, struct MLoopUV *mloopuv, + const MPoly *mpoly, const MLoop *mloop, const MLoopUV *mloopuv, unsigned int totpoly, unsigned int totvert, const float limit[2], const bool selected, const bool use_winding) { UvVertMap *vmap; UvMapVert *buf; - MPoly *mp; + const MPoly *mp; unsigned int a; int i, totuv, nverts; @@ -103,8 +103,8 @@ UvVertMap *BKE_mesh_uv_vert_map_create( nverts = mp->totloop; for (i = 0; i < nverts; i++) { - buf->tfindex = (unsigned char)i; - buf->f = a; + buf->loop_of_poly_index = (unsigned short)i; + buf->poly_index = a; buf->separate = 0; buf->next = vmap->vert[mloop[mp->loopstart + i].v]; vmap->vert[mloop[mp->loopstart + i].v] = buf; @@ -126,7 +126,8 @@ UvVertMap *BKE_mesh_uv_vert_map_create( for (a = 0; a < totvert; a++) { UvMapVert *newvlist = NULL, *vlist = vmap->vert[a]; UvMapVert *iterv, *v, *lastv, *next; - float *uv, *uv2, uvdiff[2]; + const float *uv, *uv2; + float uvdiff[2]; while (vlist) { v = vlist; @@ -134,19 +135,19 @@ UvVertMap *BKE_mesh_uv_vert_map_create( v->next = newvlist; newvlist = v; - uv = mloopuv[mpoly[v->f].loopstart + v->tfindex].uv; + uv = mloopuv[mpoly[v->poly_index].loopstart + v->loop_of_poly_index].uv; lastv = NULL; iterv = vlist; while (iterv) { next = iterv->next; - uv2 = mloopuv[mpoly[iterv->f].loopstart + iterv->tfindex].uv; + uv2 = mloopuv[mpoly[iterv->poly_index].loopstart + iterv->loop_of_poly_index].uv; sub_v2_v2v2(uvdiff, uv2, uv); if (fabsf(uv[0] - uv2[0]) < limit[0] && fabsf(uv[1] - uv2[1]) < limit[1] && - (!use_winding || winding[iterv->f] == winding[v->f])) + (!use_winding || winding[iterv->poly_index] == winding[v->poly_index])) { if (lastv) lastv->next = next; else vlist = next; diff --git a/source/blender/blenkernel/intern/node.c b/source/blender/blenkernel/intern/node.c index 7aa0ecdad87..c81f5f425d8 100644 --- a/source/blender/blenkernel/intern/node.c +++ b/source/blender/blenkernel/intern/node.c @@ -3432,6 +3432,7 @@ static void registerCompositNodes(void) register_node_type_cmp_doubleedgemask(); register_node_type_cmp_keyingscreen(); register_node_type_cmp_keying(); + register_node_type_cmp_cryptomatte(); register_node_type_cmp_translate(); register_node_type_cmp_rotate(); @@ -3520,6 +3521,7 @@ static void registerShaderNodes(void) register_node_type_sh_bsdf_velvet(); register_node_type_sh_bsdf_toon(); register_node_type_sh_bsdf_hair(); + register_node_type_sh_bsdf_hair_principled(); register_node_type_sh_emission(); register_node_type_sh_holdout(); register_node_type_sh_volume_absorption(); diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index a07e81e22b0..97c311d7f9f 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -41,6 +41,7 @@ #include "DNA_camera_types.h" #include "DNA_constraint_types.h" #include "DNA_gpencil_types.h" +#include "DNA_gpencil_modifier_types.h" #include "DNA_group_types.h" #include "DNA_groom_types.h" #include "DNA_key_types.h" @@ -54,6 +55,7 @@ #include "DNA_scene_types.h" #include "DNA_screen_types.h" #include "DNA_sequence_types.h" +#include "DNA_shader_fx_types.h" #include "DNA_smoke_types.h" #include "DNA_space_types.h" #include "DNA_view3d_types.h" @@ -88,6 +90,7 @@ #include "BKE_displist.h" #include "BKE_effect.h" #include "BKE_fcurve.h" +#include "BKE_gpencil_modifier.h" #include "BKE_icons.h" #include "BKE_key.h" #include "BKE_lamp.h" @@ -112,12 +115,14 @@ #include "BKE_rigidbody.h" #include "BKE_scene.h" #include "BKE_sequencer.h" +#include "BKE_shader_fx.h" #include "BKE_speaker.h" #include "BKE_softbody.h" #include "BKE_subsurf.h" #include "BKE_material.h" #include "BKE_camera.h" #include "BKE_image.h" +#include "BKE_gpencil.h" #include "DEG_depsgraph.h" #include "DEG_depsgraph_query.h" @@ -172,26 +177,30 @@ void BKE_object_free_softbody(Object *ob) void BKE_object_free_curve_cache(Object *ob) { - if (ob->curve_cache) { - BKE_displist_free(&ob->curve_cache->disp); - BKE_curve_bevelList_free(&ob->curve_cache->bev); - if (ob->curve_cache->path) { - free_path(ob->curve_cache->path); + if (ob->runtime.curve_cache) { + BKE_displist_free(&ob->runtime.curve_cache->disp); + BKE_curve_bevelList_free(&ob->runtime.curve_cache->bev); + if (ob->runtime.curve_cache->path) { + free_path(ob->runtime.curve_cache->path); } - BKE_nurbList_free(&ob->curve_cache->deformed_nurbs); - MEM_freeN(ob->curve_cache); - ob->curve_cache = NULL; + BKE_nurbList_free(&ob->runtime.curve_cache->deformed_nurbs); + MEM_freeN(ob->runtime.curve_cache); + ob->runtime.curve_cache = NULL; } } void BKE_object_free_modifiers(Object *ob, const int flag) { ModifierData *md; + GpencilModifierData *gp_md; while ((md = BLI_pophead(&ob->modifiers))) { modifier_free_ex(md, flag); } + while ((gp_md = BLI_pophead(&ob->greasepencil_modifiers))) { + BKE_gpencil_modifier_free_ex(gp_md, flag); + } /* particle modifiers were freed, so free the particlesystems as well */ BKE_object_free_particlesystems(ob); @@ -202,6 +211,15 @@ void BKE_object_free_modifiers(Object *ob, const int flag) BKE_object_free_derived_caches(ob); } +void BKE_object_free_shaderfx(Object *ob, const int flag) +{ + ShaderFxData *fx; + + while ((fx = BLI_pophead(&ob->shader_fx))) { + BKE_shaderfx_free_ex(fx, flag); + } +} + void BKE_object_modifier_hook_reset(Object *ob, HookModifierData *hmd) { /* reset functionality */ @@ -224,6 +242,29 @@ void BKE_object_modifier_hook_reset(Object *ob, HookModifierData *hmd) } } +void BKE_object_modifier_gpencil_hook_reset(Object *ob, HookGpencilModifierData *hmd) +{ + if (hmd->object == NULL) { + return; + } + /* reset functionality */ + bPoseChannel *pchan = BKE_pose_channel_find_name(hmd->object->pose, hmd->subtarget); + + if (hmd->subtarget[0] && pchan) { + float imat[4][4], mat[4][4]; + + /* calculate the world-space matrix for the pose-channel target first, then carry on as usual */ + mul_m4_m4m4(mat, hmd->object->obmat, pchan->pose_mat); + + invert_m4_m4(imat, mat); + mul_m4_m4m4(hmd->parentinv, imat, ob->obmat); + } + else { + invert_m4_m4(hmd->object->imat, hmd->object->obmat); + mul_m4_m4m4(hmd->parentinv, hmd->object->imat, ob->obmat); + } +} + bool BKE_object_support_modifier_type_check(const Object *ob, int modifier_type) { const ModifierTypeInfo *mti; @@ -437,6 +478,7 @@ void BKE_object_free(Object *ob) /* BKE_<id>_free shall never touch to ID->us. Never ever. */ BKE_object_free_modifiers(ob, LIB_ID_CREATE_NO_USER_REFCOUNT); + BKE_object_free_shaderfx(ob, LIB_ID_CREATE_NO_USER_REFCOUNT); MEM_SAFE_FREE(ob->mat); MEM_SAFE_FREE(ob->matbits); @@ -469,12 +511,12 @@ void BKE_object_free(Object *ob) BLI_freelistN(&ob->lodlevels); /* Free runtime curves data. */ - if (ob->curve_cache) { - BKE_curve_bevelList_free(&ob->curve_cache->bev); - if (ob->curve_cache->path) - free_path(ob->curve_cache->path); - MEM_freeN(ob->curve_cache); - ob->curve_cache = NULL; + if (ob->runtime.curve_cache) { + BKE_curve_bevelList_free(&ob->runtime.curve_cache->bev); + if (ob->runtime.curve_cache->path) + free_path(ob->runtime.curve_cache->path); + MEM_freeN(ob->runtime.curve_cache); + ob->runtime.curve_cache = NULL; } BKE_previewimg_free(&ob->preview); @@ -668,6 +710,7 @@ static const char *get_obdata_defname(int type) case OB_ARMATURE: return DATA_("Armature"); case OB_SPEAKER: return DATA_("Speaker"); case OB_EMPTY: return DATA_("Empty"); + case OB_GPENCIL: return DATA_("GPencil"); default: printf("get_obdata_defname: Internal error, bad type: %d\n", type); return DATA_("Empty"); @@ -692,6 +735,7 @@ void *BKE_object_obdata_add_from_type(Main *bmain, int type, const char *name) case OB_ARMATURE: return BKE_armature_add(bmain, name); case OB_SPEAKER: return BKE_speaker_add(bmain, name); case OB_LIGHTPROBE:return BKE_lightprobe_add(bmain, name); + case OB_GPENCIL: return BKE_gpencil_data_addnew(bmain, name); case OB_GROOM: return BKE_groom_add(bmain, name); case OB_EMPTY: return NULL; default: @@ -826,7 +870,7 @@ Object *BKE_object_add( /** * Add a new object, using another one as a reference * - * /param ob_src object to use to determine the collections of the new object. + * \param ob_src object to use to determine the collections of the new object. */ Object *BKE_object_add_from( Main *bmain, Scene *scene, ViewLayer *view_layer, @@ -844,6 +888,41 @@ Object *BKE_object_add_from( return ob; } +/** + * Add a new object, but assign the given datablock as the ob->data + * for the newly created object. + * + * \param data The datablock to assign as ob->data for the new object. + * This is assumed to be of the correct type. + * \param do_id_user If true, id_us_plus() will be called on data when + * assigning it to the object. + */ +Object *BKE_object_add_for_data( + Main *bmain, ViewLayer *view_layer, + int type, const char *name, ID *data, bool do_id_user) +{ + Object *ob; + Base *base; + LayerCollection *layer_collection; + + /* same as object_add_common, except we don't create new ob->data */ + ob = BKE_object_add_only_object(bmain, type, name); + ob->data = data; + if (do_id_user) id_us_plus(data); + + BKE_view_layer_base_deselect_all(view_layer); + DEG_id_tag_update_ex(bmain, &ob->id, OB_RECALC_OB | OB_RECALC_DATA | OB_RECALC_TIME); + + layer_collection = BKE_layer_collection_get_active(view_layer); + BKE_collection_object_add(bmain, layer_collection->collection, ob); + + base = BKE_view_layer_base_find(view_layer, ob); + BKE_view_layer_base_select(view_layer, base); + + return ob; +} + + void BKE_object_copy_softbody(struct Object *ob_dst, const struct Object *ob_src, const int flag) { SoftBody *sb = ob_src->soft; @@ -1166,9 +1245,14 @@ void BKE_object_transform_copy(Object *ob_tar, const Object *ob_src) * * \param flag Copying options (see BKE_library.h's LIB_ID_COPY_... flags for more). */ -void BKE_object_copy_data(Main *UNUSED(bmain), Object *ob_dst, const Object *ob_src, const int flag) +void BKE_object_copy_data(Main *bmain, Object *ob_dst, const Object *ob_src, const int flag) { ModifierData *md; + GpencilModifierData *gmd; + ShaderFxData *fx; + + /* Do not copy runtime data. */ + BKE_object_runtime_reset(ob_dst); /* We never handle usercount here for own data. */ const int flag_subdata = flag | LIB_ID_CREATE_NO_USER_REFCOUNT; @@ -1192,11 +1276,31 @@ void BKE_object_copy_data(Main *UNUSED(bmain), Object *ob_dst, const Object *ob_ BLI_addtail(&ob_dst->modifiers, nmd); } + BLI_listbase_clear(&ob_dst->greasepencil_modifiers); + + for (gmd = ob_src->greasepencil_modifiers.first; gmd; gmd = gmd->next) { + GpencilModifierData *nmd = BKE_gpencil_modifier_new(gmd->type); + BLI_strncpy(nmd->name, gmd->name, sizeof(nmd->name)); + BKE_gpencil_modifier_copyData_ex(gmd, nmd, flag_subdata); + BLI_addtail(&ob_dst->greasepencil_modifiers, nmd); + } + + BLI_listbase_clear(&ob_dst->shader_fx); + + for (fx = ob_src->shader_fx.first; fx; fx = fx->next) { + ShaderFxData *nfx = BKE_shaderfx_new(fx->type); + BLI_strncpy(nfx->name, fx->name, sizeof(nfx->name)); + BKE_shaderfx_copyData_ex(fx, nfx, flag_subdata); + BLI_addtail(&ob_dst->shader_fx, nfx); + } + if (ob_src->pose) { copy_object_pose(ob_dst, ob_src, flag_subdata); /* backwards compat... non-armatures can get poses in older files? */ - if (ob_src->type == OB_ARMATURE) - BKE_pose_rebuild(ob_dst, ob_dst->data); + if (ob_src->type == OB_ARMATURE) { + const bool do_pose_id_user = (flag & LIB_ID_CREATE_NO_USER_REFCOUNT) == 0; + BKE_pose_rebuild(bmain, ob_dst, ob_dst->data, do_pose_id_user); + } } defgroup_copy_list(&ob_dst->defbase, &ob_src->defbase); BKE_object_facemap_copy_list(&ob_dst->fmaps, &ob_src->fmaps); @@ -1220,18 +1324,18 @@ void BKE_object_copy_data(Main *UNUSED(bmain), Object *ob_dst, const Object *ob_ ob_dst->derivedDeform = NULL; ob_dst->derivedFinal = NULL; - BLI_listbase_clear(&ob_dst->gpulamp); BLI_listbase_clear((ListBase *)&ob_dst->drawdata); BLI_listbase_clear(&ob_dst->pc_ids); + /* grease pencil: clean derived data */ + if (ob_dst->type == OB_GPENCIL) + BKE_gpencil_free_derived_frames(ob_dst->data); + ob_dst->avs = ob_src->avs; ob_dst->mpath = animviz_copy_motionpath(ob_src->mpath); copy_object_lod(ob_dst, ob_src, flag_subdata); - /* Do not copy runtime curve data. */ - ob_dst->curve_cache = NULL; - /* Do not copy object's preview (mostly due to the fact renderers create temp copy of objects). */ if ((flag & LIB_ID_COPY_NO_PREVIEW) == 0 && false) { /* XXX TODO temp hack */ BKE_previewimg_id_copy(&ob_dst->id, &ob_src->id); @@ -1375,7 +1479,7 @@ void BKE_object_copy_proxy_drivers(Object *ob, Object *target) /* local_object->proxy == pointer to library object, saved in files and read */ /* local_object->proxy_group == pointer to collection dupli-object, saved in files and read */ -void BKE_object_make_proxy(Object *ob, Object *target, Object *cob) +void BKE_object_make_proxy(Main *bmain, Object *ob, Object *target, Object *cob) { /* paranoia checks */ if (ID_IS_LINKED(ob) || !ID_IS_LINKED(target)) { @@ -1450,7 +1554,7 @@ void BKE_object_make_proxy(Object *ob, Object *target, Object *cob) if (target->type == OB_ARMATURE) { copy_object_pose(ob, target, 0); /* data copy, object pointers in constraints */ BKE_pose_rest(ob->pose); /* clear all transforms in channels */ - BKE_pose_rebuild(ob, ob->data); /* set all internal links */ + BKE_pose_rebuild(bmain, ob, ob->data, true); /* set all internal links */ armature_set_id_extern(ob); } @@ -1486,6 +1590,11 @@ void BKE_object_obdata_size_init(struct Object *ob, const float size) ob->empty_drawsize *= size; break; } + case OB_GPENCIL: + { + ob->empty_drawsize *= size; + break; + } case OB_FONT: { Curve *cu = ob->data; @@ -1742,7 +1851,7 @@ static bool ob_parcurve(Depsgraph *depsgraph, Scene *UNUSED(scene), Object *ob, } #endif - if (par->curve_cache->path == NULL) { + if (par->runtime.curve_cache->path == NULL) { return false; } @@ -1961,10 +2070,10 @@ static void give_parvert(Object *par, int nr, float vec[3]) ListBase *nurb; /* Unless there's some weird depsgraph failure the cache should exist. */ - BLI_assert(par->curve_cache != NULL); + BLI_assert(par->runtime.curve_cache != NULL); - if (par->curve_cache->deformed_nurbs.first != NULL) { - nurb = &par->curve_cache->deformed_nurbs; + if (par->runtime.curve_cache->deformed_nurbs.first != NULL) { + nurb = &par->runtime.curve_cache->deformed_nurbs; } else { Curve *cu = par->data; @@ -1975,7 +2084,7 @@ static void give_parvert(Object *par, int nr, float vec[3]) } else if (par->type == OB_LATTICE) { Lattice *latt = par->data; - DispList *dl = par->curve_cache ? BKE_displist_find(&par->curve_cache->disp, DL_VERTS) : NULL; + DispList *dl = par->runtime.curve_cache ? BKE_displist_find(&par->runtime.curve_cache->disp, DL_VERTS) : NULL; float (*co)[3] = dl ? (float (*)[3])dl->verts : NULL; int tot; @@ -2472,7 +2581,7 @@ void BKE_object_minmax(Object *ob, float min_r[3], float max_r[3], const bool us float size[3]; copy_v3_v3(size, ob->size); - if (ob->type == OB_EMPTY) { + if ((ob->type == OB_EMPTY) || (ob->type == OB_GPENCIL)) { mul_v3_fl(size, ob->empty_drawsize); } @@ -2559,10 +2668,10 @@ void BKE_object_foreach_display_point( func_cb(co, user_data); } } - else if (ob->curve_cache && ob->curve_cache->disp.first) { + else if (ob->runtime.curve_cache && ob->runtime.curve_cache->disp.first) { DispList *dl; - for (dl = ob->curve_cache->disp.first; dl; dl = dl->next) { + for (dl = ob->runtime.curve_cache->disp.first; dl; dl = dl->next) { const float *v3 = dl->verts; int totvert = dl->nr; int i; @@ -2717,8 +2826,10 @@ void BKE_object_handle_update_ex(Depsgraph *depsgraph, * with poses we do it ahead of BKE_object_where_is_calc to ensure animation * is evaluated on the rebuilt pose, otherwise we get incorrect poses * on file load */ - if (ob->pose == NULL || (ob->pose->flag & POSE_RECALC)) - BKE_pose_rebuild(ob, ob->data); + if (ob->pose == NULL || (ob->pose->flag & POSE_RECALC)) { + /* No need to pass bmain here, we assume we do not need to rebuild DEG from here... */ + BKE_pose_rebuild(NULL, ob, ob->data, true); + } } } /* XXX new animsys warning: depsgraph tag OB_RECALC_DATA should not skip drivers, @@ -3712,6 +3823,74 @@ bool BKE_object_modifier_use_time(Object *ob, ModifierData *md) return false; } +bool BKE_object_modifier_gpencil_use_time(Object *ob, GpencilModifierData *md) +{ + if (BKE_gpencil_modifier_dependsOnTime(md)) { + return true; + } + + /* Check whether modifier is animated. */ + /* TODO (Aligorith): this should be handled as part of build_animdata() */ + if (ob->adt) { + AnimData *adt = ob->adt; + FCurve *fcu; + + char pattern[MAX_NAME + 32]; + BLI_snprintf(pattern, sizeof(pattern), "grease_pencil_modifiers[\"%s\"]", md->name); + + /* action - check for F-Curves with paths containing 'grease_pencil_modifiers[' */ + if (adt->action) { + for (fcu = adt->action->curves.first; fcu != NULL; fcu = fcu->next) { + if (fcu->rna_path && strstr(fcu->rna_path, pattern)) { + return true; + } + } + } + + /* This here allows modifier properties to get driven and still update properly */ + for (fcu = adt->drivers.first; fcu != NULL; fcu = fcu->next) { + if (fcu->rna_path && strstr(fcu->rna_path, pattern)) { + return true; + } + } + } + + return false; +} + +bool BKE_object_shaderfx_use_time(Object *ob, ShaderFxData *fx) +{ + if (BKE_shaderfx_dependsOnTime(fx)) { + return true; + } + + /* Check whether effect is animated. */ + /* TODO (Aligorith): this should be handled as part of build_animdata() */ + if (ob->adt) { + AnimData *adt = ob->adt; + FCurve *fcu; + + char pattern[MAX_NAME + 32]; + BLI_snprintf(pattern, sizeof(pattern), "shader_effects[\"%s\"]", fx->name); + + /* action - check for F-Curves with paths containing string[' */ + if (adt->action) { + for (fcu = adt->action->curves.first; fcu != NULL; fcu = fcu->next) { + if (fcu->rna_path && strstr(fcu->rna_path, pattern)) + return true; + } + } + + /* This here allows properties to get driven and still update properly */ + for (fcu = adt->drivers.first; fcu != NULL; fcu = fcu->next) { + if (fcu->rna_path && strstr(fcu->rna_path, pattern)) + return true; + } + } + + return false; +} + /* set "ignore cache" flag for all caches on this object */ static void object_cacheIgnoreClear(Object *ob, int state) { diff --git a/source/blender/blenkernel/intern/object_deform.c b/source/blender/blenkernel/intern/object_deform.c index 5c9e53aaa56..a6b0e57e55c 100644 --- a/source/blender/blenkernel/intern/object_deform.c +++ b/source/blender/blenkernel/intern/object_deform.c @@ -53,6 +53,7 @@ #include "BKE_object.h" #include "BKE_mesh.h" #include "BKE_modifier.h" +#include "BKE_gpencil.h" /** \name Misc helpers * \{ */ @@ -402,12 +403,17 @@ static void object_defgroup_remove_edit_mode(Object *ob, bDeformGroup *dg) */ void BKE_object_defgroup_remove(Object *ob, bDeformGroup *defgroup) { - if (BKE_object_is_in_editmode_vgroup(ob)) - object_defgroup_remove_edit_mode(ob, defgroup); - else - object_defgroup_remove_object_mode(ob, defgroup); + if ((ob) && (ob->type == OB_GPENCIL)) { + BKE_gpencil_vgroup_remove(ob, defgroup); + } + else { + if (BKE_object_is_in_editmode_vgroup(ob)) + object_defgroup_remove_edit_mode(ob, defgroup); + else + object_defgroup_remove_object_mode(ob, defgroup); - BKE_mesh_batch_cache_dirty(ob->data, BKE_MESH_BATCH_DIRTY_ALL); + BKE_mesh_batch_cache_dirty(ob->data, BKE_MESH_BATCH_DIRTY_ALL); + } } /** diff --git a/source/blender/blenkernel/intern/object_update.c b/source/blender/blenkernel/intern/object_update.c index 8c9569ecfcb..908debd828f 100644 --- a/source/blender/blenkernel/intern/object_update.c +++ b/source/blender/blenkernel/intern/object_update.c @@ -62,6 +62,7 @@ #include "BKE_particle.h" #include "BKE_pointcache.h" #include "BKE_scene.h" +#include "BKE_gpencil.h" #include "BKE_groom.h" #include "MEM_guardedalloc.h" @@ -325,6 +326,9 @@ void BKE_object_eval_uber_data(Depsgraph *depsgraph, case OB_MBALL: BKE_mball_batch_cache_dirty(ob->data, BKE_MBALL_BATCH_DIRTY_ALL); break; + case OB_GPENCIL: + BKE_gpencil_batch_cache_dirty(ob->data); + break; case OB_GROOM: BKE_groom_batch_cache_dirty(ob->data, BKE_GROOM_BATCH_DIRTY_ALL); break; diff --git a/source/blender/blenkernel/intern/paint.c b/source/blender/blenkernel/intern/paint.c index 07aa21f44ff..cb26f7e9f3e 100644 --- a/source/blender/blenkernel/intern/paint.c +++ b/source/blender/blenkernel/intern/paint.c @@ -41,13 +41,19 @@ #include "DNA_scene_types.h" #include "DNA_brush_types.h" #include "DNA_space_types.h" +#include "DNA_gpencil_types.h" #include "DNA_workspace_types.h" #include "BLI_bitmap.h" +#include "BLI_blenlib.h" #include "BLI_utildefines.h" +#include "BLI_string_utils.h" #include "BLI_math_vector.h" #include "BLI_listbase.h" +#include "BLT_translation.h" + +#include "BKE_animsys.h" #include "BKE_brush.h" #include "BKE_colortools.h" #include "BKE_deform.h" @@ -55,6 +61,7 @@ #include "BKE_context.h" #include "BKE_crazyspace.h" #include "BKE_global.h" +#include "BKE_gpencil.h" #include "BKE_image.h" #include "BKE_key.h" #include "BKE_library.h" @@ -151,6 +158,8 @@ Paint *BKE_paint_get_active_from_paintmode(Scene *sce, ePaintMode mode) return &ts->imapaint.paint; case ePaintSculptUV: return &ts->uvsculpt->paint; + case ePaintGpencil: + return &ts->gp_paint->paint; case ePaintInvalid: return NULL; default: @@ -176,6 +185,8 @@ Paint *BKE_paint_get_active(Scene *sce, ViewLayer *view_layer) return &ts->wpaint->paint; case OB_MODE_TEXTURE_PAINT: return &ts->imapaint.paint; + case OB_MODE_GPENCIL_PAINT: + return &ts->gp_paint->paint; case OB_MODE_EDIT: if (ts->use_uv_sculpt) return &ts->uvsculpt->paint; @@ -430,13 +441,11 @@ PaletteColor *BKE_palette_color_add(Palette *palette) return color; } - bool BKE_palette_is_empty(const struct Palette *palette) { return BLI_listbase_is_empty(&palette->colors); } - /* are we in vertex paint or weight pain face select mode? */ bool BKE_paint_select_face_test(Object *ob) { diff --git a/source/blender/blenkernel/intern/particle_system.c b/source/blender/blenkernel/intern/particle_system.c index 35c8761f671..cee1c9147b5 100644 --- a/source/blender/blenkernel/intern/particle_system.c +++ b/source/blender/blenkernel/intern/particle_system.c @@ -2941,7 +2941,7 @@ static void psys_update_path_cache(ParticleSimulationData *sim, float cfra, cons skip = 1; /* only hair, keyed and baked stuff can have paths */ else if (part->ren_as != PART_DRAW_PATH && !(part->type==PART_HAIR && ELEM(part->ren_as, PART_DRAW_OB, PART_DRAW_GR))) skip = 1; /* particle visualization must be set as path */ - else { + else if (DEG_get_mode(sim->depsgraph) != DAG_EVAL_RENDER) { if (part->draw_as != PART_DRAW_REND) skip = 1; /* draw visualization */ else if (psys->pointcache->flag & PTCACHE_BAKING) diff --git a/source/blender/blenkernel/intern/pbvh.c b/source/blender/blenkernel/intern/pbvh.c index 0a4cd3ec3c1..3e7a9b7ce24 100644 --- a/source/blender/blenkernel/intern/pbvh.c +++ b/source/blender/blenkernel/intern/pbvh.c @@ -2077,7 +2077,7 @@ static void pbvh_node_check_mask_changed(PBVH *bvh, PBVHNode *node) struct PBVHNodeDrawCallbackData { - void (*draw_fn)(void *user_data, Gwn_Batch *batch); + void (*draw_fn)(void *user_data, GPUBatch *batch); void *user_data; bool fast; }; @@ -2087,7 +2087,7 @@ static void pbvh_node_draw_cb(PBVHNode *node, void *data_v) struct PBVHNodeDrawCallbackData *data = data_v; if (!(node->flag & PBVH_FullyHidden)) { - Gwn_Batch *triangles = GPU_pbvh_buffers_batch_get(node->draw_buffers, data->fast); + GPUBatch *triangles = GPU_pbvh_buffers_batch_get(node->draw_buffers, data->fast); if (triangles != NULL) { data->draw_fn(data->user_data, triangles); } @@ -2099,7 +2099,7 @@ static void pbvh_node_draw_cb(PBVHNode *node, void *data_v) */ void BKE_pbvh_draw_cb( PBVH *bvh, float (*planes)[4], float (*fnors)[3], bool fast, - void (*draw_fn)(void *user_data, Gwn_Batch *batch), void *user_data) + void (*draw_fn)(void *user_data, GPUBatch *batch), void *user_data) { struct PBVHNodeDrawCallbackData draw_data = { .fast = fast, diff --git a/source/blender/blenkernel/intern/rigidbody.c b/source/blender/blenkernel/intern/rigidbody.c index 59847b28785..c542c3f927d 100644 --- a/source/blender/blenkernel/intern/rigidbody.c +++ b/source/blender/blenkernel/intern/rigidbody.c @@ -716,6 +716,35 @@ static void rigidbody_validate_sim_object(RigidBodyWorld *rbw, Object *ob, bool /* --------------------- */ +static void rigidbody_constraint_init_spring( + RigidBodyCon *rbc, void (*set_spring)(rbConstraint *, int, int), + void (*set_stiffness)(rbConstraint *, int, float), void (*set_damping)(rbConstraint *, int, float)) +{ + set_spring(rbc->physics_constraint, RB_LIMIT_LIN_X, rbc->flag & RBC_FLAG_USE_SPRING_X); + set_stiffness(rbc->physics_constraint, RB_LIMIT_LIN_X, rbc->spring_stiffness_x); + set_damping(rbc->physics_constraint, RB_LIMIT_LIN_X, rbc->spring_damping_x); + + set_spring(rbc->physics_constraint, RB_LIMIT_LIN_Y, rbc->flag & RBC_FLAG_USE_SPRING_Y); + set_stiffness(rbc->physics_constraint, RB_LIMIT_LIN_Y, rbc->spring_stiffness_y); + set_damping(rbc->physics_constraint, RB_LIMIT_LIN_Y, rbc->spring_damping_y); + + set_spring(rbc->physics_constraint, RB_LIMIT_LIN_Z, rbc->flag & RBC_FLAG_USE_SPRING_Z); + set_stiffness(rbc->physics_constraint, RB_LIMIT_LIN_Z, rbc->spring_stiffness_z); + set_damping(rbc->physics_constraint, RB_LIMIT_LIN_Z, rbc->spring_damping_z); + + set_spring(rbc->physics_constraint, RB_LIMIT_ANG_X, rbc->flag & RBC_FLAG_USE_SPRING_ANG_X); + set_stiffness(rbc->physics_constraint, RB_LIMIT_ANG_X, rbc->spring_stiffness_ang_x); + set_damping(rbc->physics_constraint, RB_LIMIT_ANG_X, rbc->spring_damping_ang_x); + + set_spring(rbc->physics_constraint, RB_LIMIT_ANG_Y, rbc->flag & RBC_FLAG_USE_SPRING_ANG_Y); + set_stiffness(rbc->physics_constraint, RB_LIMIT_ANG_Y, rbc->spring_stiffness_ang_y); + set_damping(rbc->physics_constraint, RB_LIMIT_ANG_Y, rbc->spring_damping_ang_y); + + set_spring(rbc->physics_constraint, RB_LIMIT_ANG_Z, rbc->flag & RBC_FLAG_USE_SPRING_ANG_Z); + set_stiffness(rbc->physics_constraint, RB_LIMIT_ANG_Z, rbc->spring_stiffness_ang_z); + set_damping(rbc->physics_constraint, RB_LIMIT_ANG_Z, rbc->spring_damping_ang_z); +} + static void rigidbody_constraint_set_limits( RigidBodyCon *rbc, void (*set_limits)(rbConstraint *, int, float, float)) { @@ -841,35 +870,24 @@ static void rigidbody_validate_sim_constraint(RigidBodyWorld *rbw, Object *ob, b RB_constraint_set_limits_piston(rbc->physics_constraint, lin_lower, lin_upper, ang_lower, ang_upper); break; case RBC_TYPE_6DOF_SPRING: - rbc->physics_constraint = RB_constraint_new_6dof_spring(loc, rot, rb1, rb2); - - RB_constraint_set_spring_6dof_spring(rbc->physics_constraint, RB_LIMIT_LIN_X, rbc->flag & RBC_FLAG_USE_SPRING_X); - RB_constraint_set_stiffness_6dof_spring(rbc->physics_constraint, RB_LIMIT_LIN_X, rbc->spring_stiffness_x); - RB_constraint_set_damping_6dof_spring(rbc->physics_constraint, RB_LIMIT_LIN_X, rbc->spring_damping_x); + if (rbc->spring_type == RBC_SPRING_TYPE2) { + rbc->physics_constraint = RB_constraint_new_6dof_spring2(loc, rot, rb1, rb2); - RB_constraint_set_spring_6dof_spring(rbc->physics_constraint, RB_LIMIT_LIN_Y, rbc->flag & RBC_FLAG_USE_SPRING_Y); - RB_constraint_set_stiffness_6dof_spring(rbc->physics_constraint, RB_LIMIT_LIN_Y, rbc->spring_stiffness_y); - RB_constraint_set_damping_6dof_spring(rbc->physics_constraint, RB_LIMIT_LIN_Y, rbc->spring_damping_y); + rigidbody_constraint_init_spring(rbc, RB_constraint_set_spring_6dof_spring2, RB_constraint_set_stiffness_6dof_spring2, RB_constraint_set_damping_6dof_spring2); - RB_constraint_set_spring_6dof_spring(rbc->physics_constraint, RB_LIMIT_LIN_Z, rbc->flag & RBC_FLAG_USE_SPRING_Z); - RB_constraint_set_stiffness_6dof_spring(rbc->physics_constraint, RB_LIMIT_LIN_Z, rbc->spring_stiffness_z); - RB_constraint_set_damping_6dof_spring(rbc->physics_constraint, RB_LIMIT_LIN_Z, rbc->spring_damping_z); + RB_constraint_set_equilibrium_6dof_spring2(rbc->physics_constraint); - RB_constraint_set_spring_6dof_spring(rbc->physics_constraint, RB_LIMIT_ANG_X, rbc->flag & RBC_FLAG_USE_SPRING_ANG_X); - RB_constraint_set_stiffness_6dof_spring(rbc->physics_constraint, RB_LIMIT_ANG_X, rbc->spring_stiffness_ang_x); - RB_constraint_set_damping_6dof_spring(rbc->physics_constraint, RB_LIMIT_ANG_X, rbc->spring_damping_ang_x); - - RB_constraint_set_spring_6dof_spring(rbc->physics_constraint, RB_LIMIT_ANG_Y, rbc->flag & RBC_FLAG_USE_SPRING_ANG_Y); - RB_constraint_set_stiffness_6dof_spring(rbc->physics_constraint, RB_LIMIT_ANG_Y, rbc->spring_stiffness_ang_y); - RB_constraint_set_damping_6dof_spring(rbc->physics_constraint, RB_LIMIT_ANG_Y, rbc->spring_damping_ang_y); + rigidbody_constraint_set_limits(rbc, RB_constraint_set_limits_6dof_spring2); + } + else { + rbc->physics_constraint = RB_constraint_new_6dof_spring(loc, rot, rb1, rb2); - RB_constraint_set_spring_6dof_spring(rbc->physics_constraint, RB_LIMIT_ANG_Z, rbc->flag & RBC_FLAG_USE_SPRING_ANG_Z); - RB_constraint_set_stiffness_6dof_spring(rbc->physics_constraint, RB_LIMIT_ANG_Z, rbc->spring_stiffness_ang_z); - RB_constraint_set_damping_6dof_spring(rbc->physics_constraint, RB_LIMIT_ANG_Z, rbc->spring_damping_ang_z); + rigidbody_constraint_init_spring(rbc, RB_constraint_set_spring_6dof_spring, RB_constraint_set_stiffness_6dof_spring, RB_constraint_set_damping_6dof_spring); - RB_constraint_set_equilibrium_6dof_spring(rbc->physics_constraint); + RB_constraint_set_equilibrium_6dof_spring(rbc->physics_constraint); - rigidbody_constraint_set_limits(rbc, RB_constraint_set_limits_6dof_spring); + rigidbody_constraint_set_limits(rbc, RB_constraint_set_limits_6dof); + } break; case RBC_TYPE_6DOF: rbc->physics_constraint = RB_constraint_new_6dof(loc, rot, rb1, rb2); @@ -1094,6 +1112,8 @@ RigidBodyCon *BKE_rigidbody_create_constraint(Scene *scene, Object *ob, short ty rbc->flag |= RBC_FLAG_ENABLED; rbc->flag |= RBC_FLAG_DISABLE_COLLISIONS; + rbc->spring_type = RBC_SPRING_TYPE2; + rbc->breaking_threshold = 10.0f; /* no good default here, just use 10 for now */ rbc->num_solver_iterations = 10; /* 10 is Bullet default */ diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c index 6a8f46badd9..8522c7f4445 100644 --- a/source/blender/blenkernel/intern/scene.c +++ b/source/blender/blenkernel/intern/scene.c @@ -113,7 +113,7 @@ #include "bmesh.h" const char *RE_engine_id_BLENDER_EEVEE = "BLENDER_EEVEE"; -const char *RE_engine_id_BLENDER_WORKBENCH = "BLENDER_WORKBENCH"; +const char *RE_engine_id_BLENDER_OPENGL = "BLENDER_OPENGL"; const char *RE_engine_id_CYCLES = "CYCLES"; void free_avicodecdata(AviCodecData *acd) @@ -173,6 +173,10 @@ ToolSettings *BKE_toolsettings_copy(ToolSettings *toolsettings, const int flag) ts->uvsculpt = MEM_dupallocN(ts->uvsculpt); BKE_paint_copy(&ts->uvsculpt->paint, &ts->uvsculpt->paint, flag); } + if (ts->gp_paint) { + ts->gp_paint = MEM_dupallocN(ts->gp_paint); + BKE_paint_copy(&ts->gp_paint->paint, &ts->gp_paint->paint, flag); + } BKE_paint_copy(&ts->imapaint.paint, &ts->imapaint.paint, flag); ts->imapaint.paintcursor = NULL; @@ -180,15 +184,10 @@ ToolSettings *BKE_toolsettings_copy(ToolSettings *toolsettings, const int flag) ts->particle.scene = NULL; ts->particle.object = NULL; - /* duplicate Grease Pencil Drawing Brushes */ - BLI_listbase_clear(&ts->gp_brushes); - for (bGPDbrush *brush = toolsettings->gp_brushes.first; brush; brush = brush->next) { - bGPDbrush *newbrush = BKE_gpencil_brush_duplicate(brush); - BLI_addtail(&ts->gp_brushes, newbrush); - } - /* duplicate Grease Pencil interpolation curve */ ts->gp_interpolate.custom_ipo = curvemapping_copy(ts->gp_interpolate.custom_ipo); + /* duplicate Grease Pencil multiframe fallof */ + ts->gp_sculpt.cur_falloff = curvemapping_copy(ts->gp_sculpt.cur_falloff); return ts; } @@ -213,16 +212,20 @@ void BKE_toolsettings_free(ToolSettings *toolsettings) BKE_paint_free(&toolsettings->uvsculpt->paint); MEM_freeN(toolsettings->uvsculpt); } + if (toolsettings->gp_paint) { + BKE_paint_free(&toolsettings->gp_paint->paint); + MEM_freeN(toolsettings->gp_paint); + } BKE_paint_free(&toolsettings->imapaint.paint); - /* free Grease Pencil Drawing Brushes */ - BKE_gpencil_free_brushes(&toolsettings->gp_brushes); - BLI_freelistN(&toolsettings->gp_brushes); - /* free Grease Pencil interpolation curve */ if (toolsettings->gp_interpolate.custom_ipo) { curvemapping_free(toolsettings->gp_interpolate.custom_ipo); } + /* free Grease Pencil multiframe falloff curve */ + if (toolsettings->gp_sculpt.cur_falloff) { + curvemapping_free(toolsettings->gp_sculpt.cur_falloff); + } MEM_freeN(toolsettings); } @@ -361,6 +364,9 @@ Scene *BKE_scene_copy(Main *bmain, Scene *sce, int type) curvemapping_copy_data(&sce_copy->r.mblur_shutter_curve, &sce->r.mblur_shutter_curve); + /* viewport display settings */ + sce_copy->display = sce->display; + /* tool settings */ sce_copy->toolsettings = BKE_toolsettings_copy(sce->toolsettings, 0); @@ -425,9 +431,9 @@ Scene *BKE_scene_copy(Main *bmain, Scene *sce, int type) } /* NOTE: part of SCE_COPY_LINK_DATA and SCE_COPY_FULL operations - * are done outside of blenkernel with ED_objects_single_users! */ + * are done outside of blenkernel with ED_object_single_users! */ - /* camera */ + /* camera */ if (ELEM(type, SCE_COPY_LINK_DATA, SCE_COPY_FULL)) { ID_NEW_REMAP(sce_copy->camera); } @@ -680,6 +686,19 @@ void BKE_scene_init(Scene *sce) sce->toolsettings->imapaint.normal_angle = 80; sce->toolsettings->imapaint.seam_bleed = 2; + /* alloc grease pencil drawing brushes */ + sce->toolsettings->gp_paint = MEM_callocN(sizeof(GpPaint), "GpPaint"); + + /* grease pencil multiframe falloff curve */ + sce->toolsettings->gp_sculpt.cur_falloff = curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); + CurveMapping *gp_falloff_curve = sce->toolsettings->gp_sculpt.cur_falloff; + curvemapping_set_defaults(gp_falloff_curve, 1, 0.0f, 0.0f, 1.0f, 1.0f); + curvemapping_initialize(gp_falloff_curve); + curvemap_reset(gp_falloff_curve->cm, + &gp_falloff_curve->clipr, + CURVE_PRESET_GAUSS, + CURVEMAP_SLOPE_POSITIVE); + sce->physics_settings.gravity[0] = 0.0f; sce->physics_settings.gravity[1] = 0.0f; sce->physics_settings.gravity[2] = -9.81f; @@ -757,46 +776,65 @@ void BKE_scene_init(Scene *sce) { GP_BrushEdit_Settings *gset = &sce->toolsettings->gp_sculpt; GP_EditBrush_Data *gp_brush; + float curcolor_add[3], curcolor_sub[3]; + ARRAY_SET_ITEMS(curcolor_add, 1.0f, 0.6f, 0.6f); + ARRAY_SET_ITEMS(curcolor_sub, 0.6f, 0.6f, 1.0f); gp_brush = &gset->brush[GP_EDITBRUSH_TYPE_SMOOTH]; gp_brush->size = 25; gp_brush->strength = 0.3f; - gp_brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF | GP_EDITBRUSH_FLAG_SMOOTH_PRESSURE; + gp_brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF | GP_EDITBRUSH_FLAG_SMOOTH_PRESSURE | GP_EDITBRUSH_FLAG_ENABLE_CURSOR; + copy_v3_v3(gp_brush->curcolor_add, curcolor_add); + copy_v3_v3(gp_brush->curcolor_sub, curcolor_sub); gp_brush = &gset->brush[GP_EDITBRUSH_TYPE_THICKNESS]; gp_brush->size = 25; gp_brush->strength = 0.5f; - gp_brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF; + gp_brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF | GP_EDITBRUSH_FLAG_ENABLE_CURSOR; + copy_v3_v3(gp_brush->curcolor_add, curcolor_add); + copy_v3_v3(gp_brush->curcolor_sub, curcolor_sub); gp_brush = &gset->brush[GP_EDITBRUSH_TYPE_STRENGTH]; gp_brush->size = 25; gp_brush->strength = 0.5f; - gp_brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF; + gp_brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF | GP_EDITBRUSH_FLAG_ENABLE_CURSOR; + copy_v3_v3(gp_brush->curcolor_add, curcolor_add); + copy_v3_v3(gp_brush->curcolor_sub, curcolor_sub); gp_brush = &gset->brush[GP_EDITBRUSH_TYPE_GRAB]; gp_brush->size = 50; gp_brush->strength = 0.3f; - gp_brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF; + gp_brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF | GP_EDITBRUSH_FLAG_ENABLE_CURSOR; + copy_v3_v3(gp_brush->curcolor_add, curcolor_add); + copy_v3_v3(gp_brush->curcolor_sub, curcolor_sub); gp_brush = &gset->brush[GP_EDITBRUSH_TYPE_PUSH]; gp_brush->size = 25; gp_brush->strength = 0.3f; - gp_brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF; + gp_brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF | GP_EDITBRUSH_FLAG_ENABLE_CURSOR; + copy_v3_v3(gp_brush->curcolor_add, curcolor_add); + copy_v3_v3(gp_brush->curcolor_sub, curcolor_sub); gp_brush = &gset->brush[GP_EDITBRUSH_TYPE_TWIST]; gp_brush->size = 50; gp_brush->strength = 0.3f; // XXX? - gp_brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF; + gp_brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF | GP_EDITBRUSH_FLAG_ENABLE_CURSOR; + copy_v3_v3(gp_brush->curcolor_add, curcolor_add); + copy_v3_v3(gp_brush->curcolor_sub, curcolor_sub); gp_brush = &gset->brush[GP_EDITBRUSH_TYPE_PINCH]; gp_brush->size = 50; gp_brush->strength = 0.5f; // XXX? - gp_brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF; + gp_brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF | GP_EDITBRUSH_FLAG_ENABLE_CURSOR; + copy_v3_v3(gp_brush->curcolor_add, curcolor_add); + copy_v3_v3(gp_brush->curcolor_sub, curcolor_sub); gp_brush = &gset->brush[GP_EDITBRUSH_TYPE_RANDOMIZE]; gp_brush->size = 25; gp_brush->strength = 0.5f; - gp_brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF; + gp_brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF | GP_EDITBRUSH_FLAG_ENABLE_CURSOR; + copy_v3_v3(gp_brush->curcolor_add, curcolor_add); + copy_v3_v3(gp_brush->curcolor_sub, curcolor_sub); } /* GP Stroke Placement */ @@ -805,6 +843,10 @@ void BKE_scene_init(Scene *sce) sce->toolsettings->gpencil_seq_align = GP_PROJECT_VIEWSPACE; sce->toolsettings->gpencil_ima_align = GP_PROJECT_VIEWSPACE; + /* Annotations */ + sce->toolsettings->annotate_v3d_align = GP_PROJECT_VIEWSPACE | GP_PROJECT_CURSOR; + sce->toolsettings->annotate_thickness = 3; + sce->orientation_index_custom = -1; /* Master Collection */ @@ -820,6 +862,9 @@ void BKE_scene_init(Scene *sce) sce->display.matcap_ssao_attenuation = 1.0f; sce->display.matcap_ssao_samples = 16; + /* OpenGL Render. */ + BKE_screen_view3d_shading_init(&sce->display.shading); + /* SceneEEVEE */ sce->eevee.gi_diffuse_bounces = 3; sce->eevee.gi_cubemap_resolution = 512; @@ -872,7 +917,6 @@ void BKE_scene_init(Scene *sce) sce->eevee.flag = SCE_EEVEE_VOLUMETRIC_LIGHTS | - SCE_EEVEE_VOLUMETRIC_COLORED | SCE_EEVEE_GTAO_BENT_NORMALS | SCE_EEVEE_GTAO_BOUNCE | SCE_EEVEE_TAA_REPROJECTION | @@ -1202,6 +1246,16 @@ char *BKE_scene_find_last_marker_name(Scene *scene, int frame) return best_marker ? best_marker->name : NULL; } +int BKE_scene_frame_snap_by_seconds(Scene *scene, double interval_in_seconds, int cfra) +{ + const int fps = round_db_to_int(FPS * interval_in_seconds); + const int second_prev = cfra - mod_i(cfra, fps); + const int second_next = second_prev + fps; + const int delta_prev = cfra - second_prev; + const int delta_next = second_next - cfra; + return (delta_prev < delta_next) ? second_prev : second_next; +} + void BKE_scene_remove_rigidbody_object(struct Main *bmain, Scene *scene, Object *ob) { /* remove rigid body constraint from world before removing object */ @@ -1282,7 +1336,7 @@ static void scene_armature_depsgraph_workaround(Main *bmain, Depsgraph *depsgrap for (ob = bmain->object.first; ob; ob = ob->id.next) { if (ob->type == OB_ARMATURE && ob->adt && ob->adt->recalc & ADT_RECALC_ANIM) { if (ob->pose == NULL || (ob->pose->flag & POSE_RECALC)) { - BKE_pose_rebuild(ob, ob->data); + BKE_pose_rebuild(bmain, ob, ob->data, true); } } } @@ -1307,7 +1361,7 @@ static bool check_rendered_viewport_visible(Main *bmain) if (area->spacetype != SPACE_VIEW3D) { continue; } - if (v3d->drawtype == OB_RENDER) { + if (v3d->shading.type == OB_RENDER) { return true; } } @@ -1423,6 +1477,18 @@ void BKE_scene_graph_update_for_newframe(Depsgraph *depsgraph, DEG_ids_clear_recalc(bmain, depsgraph); } +/** Ensures given scene/view_layer pair has a valid, up-to-date depsgraph. + * + * \warning Sets matching depsgraph as active, so should only be called from the active editing context + * (usually, from operators). + */ +void BKE_scene_view_layer_graph_evaluated_ensure(Main *bmain, Scene *scene, ViewLayer *view_layer) +{ + Depsgraph *depsgraph = BKE_scene_get_depsgraph(scene, view_layer, true); + DEG_make_active(depsgraph); + BKE_scene_graph_update_tagged(depsgraph, bmain); +} + /* return default view */ SceneRenderView *BKE_scene_add_render_view(Scene *sce, const char *name) { @@ -1541,6 +1607,11 @@ bool BKE_scene_uses_blender_eevee(const Scene *scene) return STREQ(scene->r.engine, RE_engine_id_BLENDER_EEVEE); } +bool BKE_scene_uses_blender_opengl(const Scene *scene) +{ + return STREQ(scene->r.engine, RE_engine_id_BLENDER_OPENGL); +} + bool BKE_scene_uses_cycles(const Scene *scene) { return STREQ(scene->r.engine, RE_engine_id_CYCLES); diff --git a/source/blender/blenkernel/intern/screen.c b/source/blender/blenkernel/intern/screen.c index 448b97c63b3..c107bb04e6e 100644 --- a/source/blender/blenkernel/intern/screen.c +++ b/source/blender/blenkernel/intern/screen.c @@ -45,9 +45,10 @@ #include "DNA_view3d_types.h" #include "DNA_workspace_types.h" +#include "BLI_math_vector.h" #include "BLI_listbase.h" -#include "BLI_utildefines.h" #include "BLI_rect.h" +#include "BLI_utildefines.h" #include "BKE_icons.h" #include "BKE_idprop.h" @@ -855,6 +856,22 @@ void BKE_screen_view3d_scene_sync(bScreen *sc, Scene *scene) } } +void BKE_screen_view3d_shading_init(View3DShading *shading) +{ + memset(shading, 0, sizeof(*shading)); + + shading->type = OB_SOLID; + shading->prev_type = OB_SOLID; + shading->flag = V3D_SHADING_SPECULAR_HIGHLIGHT; + shading->light = V3D_LIGHTING_STUDIO; + shading->shadow_intensity = 0.5f; + shading->xray_alpha = 0.5f; + shading->cavity_valley_factor = 1.0f; + shading->cavity_ridge_factor = 1.0f; + copy_v3_fl(shading->single_color, 0.8f); + copy_v3_fl(shading->background_color, 0.05f); +} + /* magic zoom calculation, no idea what * it signifies, if you find out, tell me! -zr */ diff --git a/source/blender/blenkernel/intern/sequencer.c b/source/blender/blenkernel/intern/sequencer.c index d857db7e276..b5b4ec5cd41 100644 --- a/source/blender/blenkernel/intern/sequencer.c +++ b/source/blender/blenkernel/intern/sequencer.c @@ -5530,6 +5530,7 @@ static Sequence *seq_dupli(const Scene *scene_src, Scene *scene_dst, Sequence *s if (scene_src == scene_dst) { if (dupe_flag & SEQ_DUPE_UNIQUE_NAME) { + /* TODO this is broken in case of Meta strips recursive duplication... Not trivial to fix. */ BKE_sequence_base_unique_name_recursive(&scene_dst->ed->seqbase, seqn); } diff --git a/source/blender/blenkernel/intern/shader_fx.c b/source/blender/blenkernel/intern/shader_fx.c new file mode 100644 index 00000000000..c028c2184fd --- /dev/null +++ b/source/blender/blenkernel/intern/shader_fx.c @@ -0,0 +1,245 @@ +/* + * ***** 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. + * + * The Original Code is Copyright (C) 2018, Blender Foundation + * This is a new part of Blender + * + * Contributor(s): Antonio Vazquez + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/blenkernel/intern/shader_fx.c + * \ingroup bke + */ + + +#include <stdio.h> + +#include "MEM_guardedalloc.h" + +#include "BLI_blenlib.h" +#include "BLI_utildefines.h" +#include "BLI_math_vector.h" +#include "BLI_string_utils.h" + +#include "BLT_translation.h" + +#include "DNA_meshdata_types.h" +#include "DNA_scene_types.h" +#include "DNA_object_types.h" +#include "DNA_gpencil_types.h" +#include "DNA_shader_fx_types.h" + +#include "BKE_global.h" +#include "BKE_library.h" +#include "BKE_library_query.h" +#include "BKE_gpencil.h" +#include "BKE_shader_fx.h" +#include "BKE_object.h" + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_query.h" + +#include "FX_shader_types.h" + +static ShaderFxTypeInfo *shader_fx_types[NUM_SHADER_FX_TYPES] = { NULL }; + +/* *************************************************** */ +/* Methods - Evaluation Loops, etc. */ + +/* check if exist grease pencil effects */ +bool BKE_shaderfx_has_gpencil(Object *ob) +{ + ShaderFxData *fx; + for (fx = ob->shader_fx.first; fx; fx = fx->next) { + const ShaderFxTypeInfo *fxi = BKE_shaderfxType_getInfo(fx->type); + if (fxi->type == eShaderFxType_GpencilType) { + return true; + } + } + return false; +} + +void BKE_shaderfx_init(void) +{ + /* Initialize shaders */ + shaderfx_type_init(shader_fx_types); /* FX_shader_util.c */ +} + +ShaderFxData *BKE_shaderfx_new(int type) +{ + const ShaderFxTypeInfo *fxi = BKE_shaderfxType_getInfo(type); + ShaderFxData *fx = MEM_callocN(fxi->struct_size, fxi->struct_name); + + /* note, this name must be made unique later */ + BLI_strncpy(fx->name, DATA_(fxi->name), sizeof(fx->name)); + + fx->type = type; + fx->mode = eShaderFxMode_Realtime | eShaderFxMode_Render | eShaderFxMode_Expanded; + fx->flag = eShaderFxFlag_StaticOverride_Local; + + if (fxi->flags & eShaderFxTypeFlag_EnableInEditmode) + fx->mode |= eShaderFxMode_Editmode; + + if (fxi->initData) fxi->initData(fx); + + return fx; +} + +static void shaderfx_free_data_id_us_cb(void *UNUSED(userData), Object *UNUSED(ob), ID **idpoin, int cb_flag) +{ + ID *id = *idpoin; + if (id != NULL && (cb_flag & IDWALK_CB_USER) != 0) { + id_us_min(id); + } +} + +void BKE_shaderfx_free_ex(ShaderFxData *fx, const int flag) +{ + const ShaderFxTypeInfo *fxi = BKE_shaderfxType_getInfo(fx->type); + + if ((flag & LIB_ID_CREATE_NO_USER_REFCOUNT) == 0) { + if (fxi->foreachIDLink) { + fxi->foreachIDLink(fx, NULL, shaderfx_free_data_id_us_cb, NULL); + } + else if (fxi->foreachObjectLink) { + fxi->foreachObjectLink(fx, NULL, (ShaderFxObjectWalkFunc)shaderfx_free_data_id_us_cb, NULL); + } + } + + if (fxi->freeData) fxi->freeData(fx); + if (fx->error) MEM_freeN(fx->error); + + MEM_freeN(fx); +} + +void BKE_shaderfx_free(ShaderFxData *fx) +{ + BKE_shaderfx_free_ex(fx, 0); +} + +/* check unique name */ +bool BKE_shaderfx_unique_name(ListBase *shaders, ShaderFxData *fx) +{ + if (shaders && fx) { + const ShaderFxTypeInfo *fxi = BKE_shaderfxType_getInfo(fx->type); + return BLI_uniquename(shaders, fx, DATA_(fxi->name), '.', offsetof(ShaderFxData, name), sizeof(fx->name)); + } + return false; +} + +bool BKE_shaderfx_dependsOnTime(ShaderFxData *fx) +{ + const ShaderFxTypeInfo *fxi = BKE_shaderfxType_getInfo(fx->type); + + return fxi->dependsOnTime && fxi->dependsOnTime(fx); +} + +const ShaderFxTypeInfo *BKE_shaderfxType_getInfo(ShaderFxType type) +{ + /* type unsigned, no need to check < 0 */ + if (type < NUM_SHADER_FX_TYPES && shader_fx_types[type]->name[0] != '\0') { + return shader_fx_types[type]; + } + else { + return NULL; + } +} + +void BKE_shaderfx_copyData_generic(const ShaderFxData *fx_src, ShaderFxData *fx_dst) +{ + const ShaderFxTypeInfo *fxi = BKE_shaderfxType_getInfo(fx_src->type); + + /* fx_dst may have alredy be fully initialized with some extra allocated data, + * we need to free it now to avoid memleak. */ + if (fxi->freeData) { + fxi->freeData(fx_dst); + } + + const size_t data_size = sizeof(ShaderFxData); + const char *fx_src_data = ((const char *)fx_src) + data_size; + char *fx_dst_data = ((char *)fx_dst) + data_size; + BLI_assert(data_size <= (size_t)fxi->struct_size); + memcpy(fx_dst_data, fx_src_data, (size_t)fxi->struct_size - data_size); +} + +static void shaderfx_copy_data_id_us_cb(void *UNUSED(userData), Object *UNUSED(ob), ID **idpoin, int cb_flag) +{ + ID *id = *idpoin; + if (id != NULL && (cb_flag & IDWALK_CB_USER) != 0) { + id_us_plus(id); + } +} + +void BKE_shaderfx_copyData_ex(ShaderFxData *fx, ShaderFxData *target, const int flag) +{ + const ShaderFxTypeInfo *fxi = BKE_shaderfxType_getInfo(fx->type); + + target->mode = fx->mode; + target->flag = fx->flag; + + if (fxi->copyData) { + fxi->copyData(fx, target); + } + + if ((flag & LIB_ID_CREATE_NO_USER_REFCOUNT) == 0) { + if (fxi->foreachIDLink) { + fxi->foreachIDLink(target, NULL, shaderfx_copy_data_id_us_cb, NULL); + } + else if (fxi->foreachObjectLink) { + fxi->foreachObjectLink(target, NULL, (ShaderFxObjectWalkFunc)shaderfx_copy_data_id_us_cb, NULL); + } + } +} + +void BKE_shaderfx_copyData(ShaderFxData *fx, ShaderFxData *target) +{ + BKE_shaderfx_copyData_ex(fx, target, 0); +} + +ShaderFxData *BKE_shaderfx_findByType(Object *ob, ShaderFxType type) +{ + ShaderFxData *fx = ob->shader_fx.first; + + for (; fx; fx = fx->next) + if (fx->type == type) + break; + + return fx; +} + +void BKE_shaderfx_foreachIDLink(Object *ob, ShaderFxIDWalkFunc walk, void *userData) +{ + ShaderFxData *fx = ob->shader_fx.first; + + for (; fx; fx = fx->next) { + const ShaderFxTypeInfo *fxi = BKE_shaderfxType_getInfo(fx->type); + + if (fxi->foreachIDLink) fxi->foreachIDLink(fx, ob, walk, userData); + else if (fxi->foreachObjectLink) { + /* each Object can masquerade as an ID, so this should be OK */ + ShaderFxObjectWalkFunc fp = (ShaderFxObjectWalkFunc)walk; + fxi->foreachObjectLink(fx, ob, fp, userData); + } + } +} + +ShaderFxData *BKE_shaderfx_findByName(Object *ob, const char *name) +{ + return BLI_findstring(&(ob->shader_fx), name, offsetof(ShaderFxData, name)); +} diff --git a/source/blender/blenkernel/intern/sound.c b/source/blender/blenkernel/intern/sound.c index ebb23f8d5c6..5135362bd69 100644 --- a/source/blender/blenkernel/intern/sound.c +++ b/source/blender/blenkernel/intern/sound.c @@ -756,15 +756,19 @@ int BKE_sound_scene_playing(struct Scene *scene) void BKE_sound_free_waveform(bSound *sound) { - SoundWaveform *waveform = sound->waveform; - if (waveform) { - if (waveform->data) { - MEM_freeN(waveform->data); + if ((sound->tags & SOUND_TAGS_WAVEFORM_NO_RELOAD) == 0) { + SoundWaveform *waveform = sound->waveform; + if (waveform) { + if (waveform->data) { + MEM_freeN(waveform->data); + } + MEM_freeN(waveform); } - MEM_freeN(waveform); - } - sound->waveform = NULL; + sound->waveform = NULL; + } + /* This tag is only valid once. */ + sound->tags &= ~SOUND_TAGS_WAVEFORM_NO_RELOAD; } void BKE_sound_read_waveform(bSound *sound, short *stop) @@ -793,7 +797,7 @@ void BKE_sound_read_waveform(bSound *sound, short *stop) } MEM_freeN(waveform); BLI_spin_lock(sound->spinlock); - sound->flags &= ~SOUND_FLAGS_WAVEFORM_LOADING; + sound->tags &= ~SOUND_TAGS_WAVEFORM_LOADING; BLI_spin_unlock(sound->spinlock); return; } @@ -802,7 +806,7 @@ void BKE_sound_read_waveform(bSound *sound, short *stop) BLI_spin_lock(sound->spinlock); sound->waveform = waveform; - sound->flags &= ~SOUND_FLAGS_WAVEFORM_LOADING; + sound->tags &= ~SOUND_TAGS_WAVEFORM_LOADING; BLI_spin_unlock(sound->spinlock); } diff --git a/source/blender/blenkernel/intern/studiolight.c b/source/blender/blenkernel/intern/studiolight.c index 200c50e5c23..df5cc7551aa 100644 --- a/source/blender/blenkernel/intern/studiolight.c +++ b/source/blender/blenkernel/intern/studiolight.c @@ -442,106 +442,64 @@ static void studiolight_calculate_spherical_harmonics_coefficient(StudioLight *s switch (sh_component) { /* L0 */ case 0: - { coef = 0.2822095f; break; - } - /* L1 */ case 1: - { coef = -0.488603f * nz * 2.0f / 3.0f; break; - } case 2: - { coef = 0.488603f * ny * 2.0f / 3.0f; break; - } case 3: - { coef = -0.488603f * nx * 2.0f / 3.0f; break; - } - /* L2 */ case 4: - { coef = 1.092548f * nx * nz * 1.0f / 4.0f; break; - } case 5: - { coef = -1.092548f * nz * ny * 1.0f / 4.0f; break; - } case 6: - { coef = 0.315392f * (3.0f * ny2 - 1.0f) * 1.0f / 4.0f; break; - } case 7: - { coef = 1.092548f * nx * ny * 1.0f / 4.0f; break; - } case 8: - { coef = 0.546274f * (nx2 - nz2) * 1.0f / 4.0f; break; - } - /* L4 */ case 9: - { coef = (2.5033429417967046f * nx * nz * (nx2 - nz2)) / -24.0f; break; - } case 10: - { coef = (-1.7701307697799304f * nz * ny * (3.0f * nx2 - nz2)) / -24.0f; break; - } case 11: - { coef = (0.9461746957575601f * nz * nx * (-1.0f + 7.0f * ny2)) / -24.0f; break; - } case 12: - { coef = (-0.6690465435572892f * nz * ny * (-3.0f + 7.0f * ny2)) / -24.0f; break; - } case 13: - { coef = ((105.0f * ny4 - 90.0f * ny2 + 9.0f) / 28.359261614f) / -24.0f; break; - } case 14: - { coef = (-0.6690465435572892f * nx * ny * (-3.0f + 7.0f * ny2)) / -24.0f; break; - } case 15: - { coef = (0.9461746957575601f * (nx2 - nz2) * (-1.0f + 7.0f * ny2)) / -24.0f; break; - } case 16: - { coef = (-1.7701307697799304f * nx * ny * (nx2 - 3.0f * nz2)) / -24.0f; break; - } case 17: - { coef = (0.6258357354491761f * (nx4 - 6.0f * nz2 * nx2 + nz4)) / -24.0f; break; - } - default: - { coef = 0.0f; - } } madd_v3_v3fl(sh, color, coef * weight); @@ -1037,7 +995,7 @@ static void studiolight_matcap_preview(uint *icon_buffer, StudioLight *sl, bool if (flipped) { fx = 1.0f - fx; } - nearest_interpolation_color(ibuf, NULL, color, fx * ibuf->x, fy * ibuf->y); + nearest_interpolation_color(ibuf, NULL, color, fx * ibuf->x - 1.0f, fy * ibuf->y - 1.0f); uint alphamask = alpha_circle_mask(fx, fy, 0.5f - pixel_size, 0.5f); diff --git a/source/blender/blenkernel/intern/subdiv.c b/source/blender/blenkernel/intern/subdiv.c new file mode 100644 index 00000000000..bae5491c079 --- /dev/null +++ b/source/blender/blenkernel/intern/subdiv.c @@ -0,0 +1,114 @@ +/* + * ***** 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. + * + * The Original Code is Copyright (C) 2018 by Blender Foundation. + * All rights reserved. + * + * Contributor(s): Sergey Sharybin. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/blenkernel/intern/subdiv.c + * \ingroup bke + */ + +#include "BKE_subdiv.h" + +#include "DNA_mesh_types.h" + +#include "BLI_utildefines.h" + +#include "MEM_guardedalloc.h" + +#include "subdiv_converter.h" + +#ifdef WITH_OPENSUBDIV +# include "opensubdiv_capi.h" +# include "opensubdiv_converter_capi.h" +# include "opensubdiv_evaluator_capi.h" +# include "opensubdiv_topology_refiner_capi.h" +#endif + +Subdiv *BKE_subdiv_new_from_converter(const SubdivSettings *settings, + struct OpenSubdiv_Converter *converter) +{ +#ifdef WITH_OPENSUBDIV + SubdivStats stats; + BKE_subdiv_stats_init(&stats); + BKE_subdiv_stats_begin(&stats, SUBDIV_STATS_TOPOLOGY_REFINER_CREATION_TIME); + OpenSubdiv_TopologyRefinerSettings topology_refiner_settings; + topology_refiner_settings.level = settings->level; + topology_refiner_settings.is_adaptive = settings->is_adaptive; + struct OpenSubdiv_TopologyRefiner *osd_topology_refiner = NULL; + if (converter->getNumVertices(converter) != 0) { + osd_topology_refiner = + openSubdiv_createTopologyRefinerFromConverter( + converter, &topology_refiner_settings); + + } + else { + /* TODO(sergey): Check whether original geometry had any vertices. + * The thing here is: OpenSubdiv can only deal with faces, but our + * side of subdiv also deals with loose vertices and edges. + */ + } + Subdiv *subdiv = MEM_callocN(sizeof(Subdiv), "subdiv from converetr"); + subdiv->settings = *settings; + subdiv->topology_refiner = osd_topology_refiner; + subdiv->evaluator = NULL; + BKE_subdiv_stats_end(&stats, SUBDIV_STATS_TOPOLOGY_REFINER_CREATION_TIME); + subdiv->stats = stats; + return subdiv; +#else + UNUSED_VARS(settings, converter); + return NULL; +#endif +} + +Subdiv *BKE_subdiv_new_from_mesh(const SubdivSettings *settings, + struct Mesh *mesh) +{ +#ifdef WITH_OPENSUBDIV + if (mesh->totvert == 0) { + return NULL; + } + OpenSubdiv_Converter converter; + BKE_subdiv_converter_init_for_mesh(&converter, settings, mesh); + Subdiv *subdiv = BKE_subdiv_new_from_converter(settings, &converter); + BKE_subdiv_converter_free(&converter); + return subdiv; +#else + UNUSED_VARS(settings, mesh); + return NULL; +#endif +} + +void BKE_subdiv_free(Subdiv *subdiv) +{ +#ifdef WITH_OPENSUBDIV + if (subdiv->evaluator != NULL) { + openSubdiv_deleteEvaluator(subdiv->evaluator); + } + if (subdiv->topology_refiner != NULL) { + openSubdiv_deleteTopologyRefiner(subdiv->topology_refiner); + } + MEM_freeN(subdiv); +#else + UNUSED_VARS(subdiv); +#endif +} diff --git a/source/blender/blenkernel/intern/subdiv_converter.c b/source/blender/blenkernel/intern/subdiv_converter.c new file mode 100644 index 00000000000..f6dabfa1c80 --- /dev/null +++ b/source/blender/blenkernel/intern/subdiv_converter.c @@ -0,0 +1,65 @@ +/* + * ***** 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. + * + * The Original Code is Copyright (C) 2018 by Blender Foundation. + * All rights reserved. + * + * Contributor(s): Sergey Sharybin. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include "subdiv_converter.h" + +#include "BLI_utildefines.h" + +#ifdef WITH_OPENSUBDIV +# include "opensubdiv_converter_capi.h" +#endif + +void BKE_subdiv_converter_free(struct OpenSubdiv_Converter *converter) +{ +#ifdef WITH_OPENSUBDIV + if (converter->freeUserData) { + converter->freeUserData(converter); + } +#else + UNUSED_VARS(converter); +#endif +} + +/*OpenSubdiv_FVarLinearInterpolation*/ int +BKE_subdiv_converter_fvar_linear_from_settings(const SubdivSettings *settings) +{ +#ifdef WITH_OPENSUBDIV + switch (settings->fvar_linear_interpolation) { + case SUBDIV_FVAR_LINEAR_INTERPOLATION_NONE: + return OSD_FVAR_LINEAR_INTERPOLATION_NONE; + case SUBDIV_FVAR_LINEAR_INTERPOLATION_CORNERS_ONLY: + return OSD_FVAR_LINEAR_INTERPOLATION_CORNERS_ONLY; + case SUBDIV_FVAR_LINEAR_INTERPOLATION_BOUNDARIES: + return OSD_FVAR_LINEAR_INTERPOLATION_BOUNDARIES; + case SUBDIV_FVAR_LINEAR_INTERPOLATION_ALL: + return OSD_FVAR_LINEAR_INTERPOLATION_ALL; + } + BLI_assert(!"Unknown fvar linear interpolation"); + return OSD_FVAR_LINEAR_INTERPOLATION_NONE; +#else + UNUSED_VARS(settings); + return 0; +#endif +} diff --git a/source/blender/blenkernel/intern/subdiv_converter.h b/source/blender/blenkernel/intern/subdiv_converter.h new file mode 100644 index 00000000000..4c552a9164e --- /dev/null +++ b/source/blender/blenkernel/intern/subdiv_converter.h @@ -0,0 +1,57 @@ +/* + * ***** 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. + * + * The Original Code is Copyright (C) 2018 by Blender Foundation. + * All rights reserved. + * + * Contributor(s): Sergey Sharybin. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __SUBDIV_CONVERTER_H__ +#define __SUBDIV_CONVERTER_H__ + +#include "BKE_subdiv.h" + +/* NOTE: Was initially used to get proper enumerator types, but this makes + * it tricky to compile without OpenSubdiv. + */ +/* #include "opensubdiv_converter_capi.h" */ + +struct Mesh; +struct OpenSubdiv_Converter; +struct SubdivSettings; + +void BKE_subdiv_converter_init_for_mesh(struct OpenSubdiv_Converter *converter, + const struct SubdivSettings *settings, + const struct Mesh *mesh); + +/* NOTE: Frees converter data, but not converter itself. This means, that if + * converter was allocated on heap, it is up to the user to free that memory. + */ +void BKE_subdiv_converter_free(struct OpenSubdiv_Converter *converter); + +/* ============================ INTERNAL HELPERS ============================ */ + +/* TODO(sergey): Find a way to make it OpenSubdiv_FVarLinearInterpolation, + * without breaking compilation without OpenSubdiv. + */ +int BKE_subdiv_converter_fvar_linear_from_settings( + const SubdivSettings *settings); + +#endif /* __SUBDIV_CONVERTER_H__ */ diff --git a/source/blender/blenkernel/intern/subdiv_converter_mesh.c b/source/blender/blenkernel/intern/subdiv_converter_mesh.c new file mode 100644 index 00000000000..c5fb8afb6cd --- /dev/null +++ b/source/blender/blenkernel/intern/subdiv_converter_mesh.c @@ -0,0 +1,406 @@ +/* + * ***** 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. + * + * The Original Code is Copyright (C) 2018 by Blender Foundation. + * All rights reserved. + * + * Contributor(s): Sergey Sharybin. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include "subdiv_converter.h" + +#include <string.h> + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "BLI_utildefines.h" +#include "BLI_bitmap.h" +#include "BLI_math_vector.h" + +#include "BKE_customdata.h" +#include "BKE_mesh_mapping.h" +#include "BKE_subdiv.h" + +#include "MEM_guardedalloc.h" + +#ifdef WITH_OPENSUBDIV +# include "opensubdiv_capi.h" +# include "opensubdiv_converter_capi.h" +#endif + +#ifdef WITH_OPENSUBDIV +typedef struct ConverterStorage { + SubdivSettings settings; + const Mesh *mesh; + /* Indexed by loop index, value denotes index of face-varying vertex + * which corresponds to the UV coordinate. + */ + int *loop_uv_indices; + int num_uv_coordinates; + /* Indexed by coarse mesh elements, gives index of corresponding element + * with ignoring all non-manifold entities. + * + * NOTE: This isn't strictly speaking manifold, this is more like non-loose + * geometry index. As in, index of element as if there were no loose edges + * or vertices in the mesh. + */ + int *manifold_vertex_index; + /* Indexed by vertex index from mesh, corresponds to whether this vertex has + * infinite sharpness due to non-manifol topology. + */ + BLI_bitmap *infinite_sharp_vertices_map; + /* Reverse mapping to above. */ + int *manifold_vertex_index_reverse; + int *manifold_edge_index_reverse; + /* Number of non-loose elements. */ + int num_manifold_vertices; + int num_manifold_edges; +} ConverterStorage; + +static OpenSubdiv_SchemeType get_scheme_type( + const OpenSubdiv_Converter *converter) +{ + ConverterStorage *storage = converter->user_data; + if (storage->settings.is_simple) { + return OSD_SCHEME_BILINEAR; + } + else { + return OSD_SCHEME_CATMARK; + } +} + +static OpenSubdiv_FVarLinearInterpolation get_fvar_linear_interpolation( + const OpenSubdiv_Converter *converter) +{ + ConverterStorage *storage = converter->user_data; + return BKE_subdiv_converter_fvar_linear_from_settings(&storage->settings); +} + +static bool specifies_full_topology( + const OpenSubdiv_Converter *UNUSED(converter)) +{ + return false; +} + +static int get_num_faces(const OpenSubdiv_Converter *converter) +{ + ConverterStorage *storage = converter->user_data; + return storage->mesh->totpoly; +} + +static int get_num_edges(const OpenSubdiv_Converter *converter) +{ + ConverterStorage *storage = converter->user_data; + return storage->num_manifold_edges; +} + +static int get_num_vertices(const OpenSubdiv_Converter *converter) +{ + ConverterStorage *storage = converter->user_data; + return storage->num_manifold_vertices; +} + +static int get_num_face_vertices(const OpenSubdiv_Converter *converter, + int manifold_face_index) +{ + ConverterStorage *storage = converter->user_data; + return storage->mesh->mpoly[manifold_face_index].totloop; +} + +static void get_face_vertices(const OpenSubdiv_Converter *converter, + int manifold_face_index, + int *manifold_face_vertices) +{ + ConverterStorage *storage = converter->user_data; + const MPoly *poly = &storage->mesh->mpoly[manifold_face_index]; + const MLoop *mloop = storage->mesh->mloop; + for (int corner = 0; corner < poly->totloop; corner++) { + manifold_face_vertices[corner] = storage->manifold_vertex_index[ + mloop[poly->loopstart + corner].v]; + } +} + +static void get_edge_vertices(const OpenSubdiv_Converter *converter, + int manifold_edge_index, + int *manifold_edge_vertices) +{ + ConverterStorage *storage = converter->user_data; + const int edge_index = + storage->manifold_edge_index_reverse[manifold_edge_index]; + const MEdge *edge = &storage->mesh->medge[edge_index]; + manifold_edge_vertices[0] = storage->manifold_vertex_index[edge->v1]; + manifold_edge_vertices[1] = storage->manifold_vertex_index[edge->v2]; +} + +static float get_edge_sharpness(const OpenSubdiv_Converter *converter, + int manifold_edge_index) +{ + ConverterStorage *storage = converter->user_data; + const int edge_index = + storage->manifold_edge_index_reverse[manifold_edge_index]; + const MEdge *medge = storage->mesh->medge; + const float edge_crease = (float)medge[edge_index].crease / 255.0f; + return edge_crease * edge_crease * 10.0f; +} + + +static bool is_infinite_sharp_vertex(const OpenSubdiv_Converter *converter, + int manifold_vertex_index) +{ + ConverterStorage *storage = converter->user_data; + const int vertex_index = + storage->manifold_vertex_index_reverse[manifold_vertex_index]; + return BLI_BITMAP_TEST_BOOL(storage->infinite_sharp_vertices_map, + vertex_index); +} + +static float get_vertex_sharpness(const OpenSubdiv_Converter *UNUSED(converter), + int UNUSED(manifold_vertex_index)) +{ + return 0.0f; +} + +static int get_num_uv_layers(const OpenSubdiv_Converter *converter) +{ + ConverterStorage *storage = converter->user_data; + const Mesh *mesh = storage->mesh; + return CustomData_number_of_layers(&mesh->ldata, CD_MLOOPUV); +} + +static void precalc_uv_layer(const OpenSubdiv_Converter *converter, + const int layer_index) +{ + ConverterStorage *storage = converter->user_data; + const Mesh *mesh = storage->mesh; + const MPoly *mpoly = mesh->mpoly; + const MLoop *mloop = mesh->mloop; + const MLoopUV *mloopuv = CustomData_get_layer_n( + &mesh->ldata, CD_MLOOPUV, layer_index); + const int num_poly = mesh->totpoly; + const int num_vert = mesh->totvert; + const float limit[2] = {STD_UV_CONNECT_LIMIT, STD_UV_CONNECT_LIMIT}; + /* Initialize memory required for the operations. */ + if (storage->loop_uv_indices == NULL) { + storage->loop_uv_indices = MEM_malloc_arrayN( + mesh->totloop, sizeof(int), "loop uv vertex index"); + } + UvVertMap *uv_vert_map = BKE_mesh_uv_vert_map_create( + mpoly, mloop, mloopuv, + num_poly, num_vert, + limit, + false, true); + /* NOTE: First UV vertex is supposed to be always marked as separate. */ + storage->num_uv_coordinates = -1; + for (int vertex_index = 0; vertex_index < num_vert; ++vertex_index) { + const UvMapVert *uv_vert = BKE_mesh_uv_vert_map_get_vert(uv_vert_map, + vertex_index); + while (uv_vert != NULL) { + if (uv_vert->separate) { + storage->num_uv_coordinates++; + } + const MPoly *mp = &mpoly[uv_vert->poly_index]; + const int global_loop_index = mp->loopstart + + uv_vert->loop_of_poly_index; + storage->loop_uv_indices[global_loop_index] = + storage->num_uv_coordinates; + uv_vert = uv_vert->next; + } + } + /* So far this value was used as a 0-based index, actual number of UV + * vertices is 1 more. + */ + storage->num_uv_coordinates += 1; + BKE_mesh_uv_vert_map_free(uv_vert_map); +} + +static void finish_uv_layer(const OpenSubdiv_Converter *UNUSED(converter)) +{ +} + +static int get_num_uvs(const OpenSubdiv_Converter *converter) +{ + ConverterStorage *storage = converter->user_data; + return storage->num_uv_coordinates; +} + +static int get_face_corner_uv_index(const OpenSubdiv_Converter *converter, + const int face_index, + const int corner) +{ + ConverterStorage *storage = converter->user_data; + const MPoly *mp = &storage->mesh->mpoly[face_index]; + return storage->loop_uv_indices[mp->loopstart + corner]; +} + +static void free_user_data(const OpenSubdiv_Converter *converter) +{ + ConverterStorage *user_data = converter->user_data; + MEM_SAFE_FREE(user_data->loop_uv_indices); + MEM_freeN(user_data->manifold_vertex_index); + MEM_freeN(user_data->infinite_sharp_vertices_map); + MEM_freeN(user_data->manifold_vertex_index_reverse); + MEM_freeN(user_data->manifold_edge_index_reverse); + MEM_freeN(user_data); +} + +static void init_functions(OpenSubdiv_Converter *converter) +{ + converter->getSchemeType = get_scheme_type; + converter->getFVarLinearInterpolation = get_fvar_linear_interpolation; + converter->specifiesFullTopology = specifies_full_topology; + + converter->getNumFaces = get_num_faces; + converter->getNumEdges = get_num_edges; + converter->getNumVertices = get_num_vertices; + + converter->getNumFaceVertices = get_num_face_vertices; + converter->getFaceVertices = get_face_vertices; + converter->getFaceEdges = NULL; + + converter->getEdgeVertices = get_edge_vertices; + converter->getNumEdgeFaces = NULL; + converter->getEdgeFaces = NULL; + converter->getEdgeSharpness = get_edge_sharpness; + + converter->getNumVertexEdges = NULL; + converter->getVertexEdges = NULL; + converter->getNumVertexFaces = NULL; + converter->getVertexFaces = NULL; + converter->isInfiniteSharpVertex = is_infinite_sharp_vertex; + converter->getVertexSharpness = get_vertex_sharpness; + + converter->getNumUVLayers = get_num_uv_layers; + converter->precalcUVLayer = precalc_uv_layer; + converter->finishUVLayer = finish_uv_layer; + converter->getNumUVCoordinates = get_num_uvs; + converter->getFaceCornerUVIndex = get_face_corner_uv_index; + + converter->freeUserData = free_user_data; +} + +static void initialize_manifold_index_array(const BLI_bitmap *used_map, + const int num_elements, + int **indices_r, + int **indices_reverse_r, + int *num_manifold_elements_r) +{ + int *indices = NULL; + if (indices_r != NULL) { + indices = MEM_malloc_arrayN( + num_elements, sizeof(int), "manifold indices"); + } + int *indices_reverse = NULL; + if (indices_reverse_r != NULL) { + indices_reverse = MEM_malloc_arrayN( + num_elements, sizeof(int), "manifold indices reverse"); + } + int offset = 0; + for (int i = 0; i < num_elements; i++) { + if (BLI_BITMAP_TEST_BOOL(used_map, i)) { + if (indices != NULL) { + indices[i] = i - offset; + } + if (indices_reverse != NULL) { + indices_reverse[i - offset] = i; + } + } + else { + if (indices != NULL) { + indices[i] = -1; + } + offset++; + } + } + if (indices_r != NULL) { + *indices_r = indices; + } + if (indices_reverse_r != NULL) { + *indices_reverse_r = indices_reverse; + } + *num_manifold_elements_r = num_elements - offset; +} + +static void initialize_manifold_indices(ConverterStorage *storage) +{ + const Mesh *mesh = storage->mesh; + const MEdge *medge = mesh->medge; + const MLoop *mloop = mesh->mloop; + const MPoly *mpoly = mesh->mpoly; + /* Set bits of elements which are not loose. */ + BLI_bitmap *vert_used_map = BLI_BITMAP_NEW(mesh->totvert, "vert used map"); + BLI_bitmap *edge_used_map = BLI_BITMAP_NEW(mesh->totedge, "edge used map"); + for (int poly_index = 0; poly_index < mesh->totpoly; poly_index++) { + const MPoly *poly = &mpoly[poly_index]; + for (int corner = 0; corner < poly->totloop; corner++) { + const MLoop *loop = &mloop[poly->loopstart + corner]; + BLI_BITMAP_ENABLE(vert_used_map, loop->v); + BLI_BITMAP_ENABLE(edge_used_map, loop->e); + } + } + initialize_manifold_index_array(vert_used_map, + mesh->totvert, + &storage->manifold_vertex_index, + &storage->manifold_vertex_index_reverse, + &storage->num_manifold_vertices); + initialize_manifold_index_array(edge_used_map, + mesh->totedge, + NULL, + &storage->manifold_edge_index_reverse, + &storage->num_manifold_edges); + /* Initialize infinite sharp mapping. */ + storage->infinite_sharp_vertices_map = + BLI_BITMAP_NEW(mesh->totvert, "vert used map"); + for (int edge_index = 0; edge_index < mesh->totedge; edge_index++) { + if (!BLI_BITMAP_TEST_BOOL(edge_used_map, edge_index)) { + const MEdge *edge = &medge[edge_index]; + BLI_BITMAP_ENABLE(storage->infinite_sharp_vertices_map, edge->v1); + BLI_BITMAP_ENABLE(storage->infinite_sharp_vertices_map, edge->v2); + } + } + /* Free working variables. */ + MEM_freeN(vert_used_map); + MEM_freeN(edge_used_map); +} + +static void init_user_data(OpenSubdiv_Converter *converter, + const SubdivSettings *settings, + const Mesh *mesh) +{ + ConverterStorage *user_data = + MEM_mallocN(sizeof(ConverterStorage), __func__); + user_data->settings = *settings; + user_data->mesh = mesh; + user_data->loop_uv_indices = NULL; + initialize_manifold_indices(user_data); + converter->user_data = user_data; +} +#endif + +void BKE_subdiv_converter_init_for_mesh(struct OpenSubdiv_Converter *converter, + const SubdivSettings *settings, + const Mesh *mesh) +{ +#ifdef WITH_OPENSUBDIV + init_functions(converter); + init_user_data(converter, settings, mesh); +#else + UNUSED_VARS(converter, settings, mesh); +#endif +} diff --git a/source/blender/blenkernel/intern/subdiv_eval.c b/source/blender/blenkernel/intern/subdiv_eval.c new file mode 100644 index 00000000000..e23be84ee26 --- /dev/null +++ b/source/blender/blenkernel/intern/subdiv_eval.c @@ -0,0 +1,361 @@ +/* + * ***** 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. + * + * The Original Code is Copyright (C) 2018 by Blender Foundation. + * All rights reserved. + * + * Contributor(s): Sergey Sharybin. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/blenkernel/intern/subdiv_eval.c + * \ingroup bke + */ + +#include "BKE_subdiv.h" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "BLI_utildefines.h" +#include "BLI_bitmap.h" +#include "BLI_math_vector.h" + +#include "BKE_customdata.h" + +#include "MEM_guardedalloc.h" + +#ifdef WITH_OPENSUBDIV +# include "opensubdiv_evaluator_capi.h" +# include "opensubdiv_topology_refiner_capi.h" +#endif + +void BKE_subdiv_eval_begin(Subdiv *subdiv) +{ +#ifdef WITH_OPENSUBDIV + if (subdiv->topology_refiner == NULL) { + /* Happens on input mesh with just loose geometry. */ + } + else if (subdiv->evaluator == NULL) { + BKE_subdiv_stats_begin(&subdiv->stats, SUBDIV_STATS_EVALUATOR_CREATE); + subdiv->evaluator = openSubdiv_createEvaluatorFromTopologyRefiner( + subdiv->topology_refiner); + BKE_subdiv_stats_end(&subdiv->stats, SUBDIV_STATS_EVALUATOR_CREATE); + } + else { + /* TODO(sergey): Check for topology change. */ + } +#else + UNUSED_VARS(subdiv); +#endif +} + +#ifdef WITH_OPENSUBDIV +static void set_coarse_positions(Subdiv *subdiv, const Mesh *mesh) +{ + const MVert *mvert = mesh->mvert; + const MLoop *mloop = mesh->mloop; + const MPoly *mpoly = mesh->mpoly; + /* Mark vertices which needs new coordinates. */ + /* TODO(sergey): This is annoying to calculate this on every update, + * maybe it's better to cache this mapping. Or make it possible to have + * OpenSubdiv's vertices match mesh ones? + */ + BLI_bitmap *vertex_used_map = + BLI_BITMAP_NEW(mesh->totvert, "vert used map"); + for (int poly_index = 0; poly_index < mesh->totpoly; poly_index++) { + const MPoly *poly = &mpoly[poly_index]; + for (int corner = 0; corner < poly->totloop; corner++) { + const MLoop *loop = &mloop[poly->loopstart + corner]; + BLI_BITMAP_ENABLE(vertex_used_map, loop->v); + } + } + for (int vertex_index = 0, manifold_veretx_index = 0; + vertex_index < mesh->totvert; + vertex_index++) + { + if (!BLI_BITMAP_TEST_BOOL(vertex_used_map, vertex_index)) { + continue; + } + const MVert *vertex = &mvert[vertex_index]; + subdiv->evaluator->setCoarsePositions( + subdiv->evaluator, + vertex->co, + manifold_veretx_index, 1); + manifold_veretx_index++; + } + MEM_freeN(vertex_used_map); +} + +static void set_face_varying_data_from_uv(Subdiv *subdiv, + const MLoopUV *mloopuv, + const int layer_index) +{ + OpenSubdiv_TopologyRefiner *topology_refiner = subdiv->topology_refiner; + OpenSubdiv_Evaluator *evaluator = subdiv->evaluator; + const int num_faces = topology_refiner->getNumFaces(topology_refiner); + const MLoopUV *mluv = mloopuv; + /* TODO(sergey): OpenSubdiv's C-API converter can change winding of + * loops of a face, need to watch for that, to prevent wrong UVs assigned. + */ + for (int face_index = 0; face_index < num_faces; ++face_index) { + const int num_face_vertices = topology_refiner->getNumFaceVertices( + topology_refiner, face_index); + const int *uv_indicies = topology_refiner->getFaceFVarValueIndices( + topology_refiner, face_index, layer_index); + for (int vertex_index = 0; + vertex_index < num_face_vertices; + vertex_index++, mluv++) + { + evaluator->setFaceVaryingData(evaluator, + layer_index, + mluv->uv, + uv_indicies[vertex_index], + 1); + } + } +} +#endif + +void BKE_subdiv_eval_update_from_mesh(Subdiv *subdiv, const Mesh *mesh) +{ +#ifdef WITH_OPENSUBDIV + BKE_subdiv_eval_begin(subdiv); + if (subdiv->evaluator == NULL) { + return; + } + /* Set coordinates of base mesh vertices. */ + set_coarse_positions(subdiv, mesh); + /* Set face-varyign data to UV maps. */ + const int num_uv_layers = + CustomData_number_of_layers(&mesh->ldata, CD_MLOOPUV); + for (int layer_index = 0; layer_index < num_uv_layers; layer_index++) { + const MLoopUV *mloopuv = CustomData_get_layer_n( + &mesh->ldata, CD_MLOOPUV, layer_index); + set_face_varying_data_from_uv(subdiv, mloopuv, layer_index); + } + /* Update evaluator to the new coarse geometry. */ + BKE_subdiv_stats_begin(&subdiv->stats, SUBDIV_STATS_EVALUATOR_REFINE); + subdiv->evaluator->refine(subdiv->evaluator); + BKE_subdiv_stats_end(&subdiv->stats, SUBDIV_STATS_EVALUATOR_REFINE); +#else + UNUSED_VARS(subdiv, mesh); +#endif +} + +/* ========================== Single point queries ========================== */ + +void BKE_subdiv_eval_limit_point( + Subdiv *subdiv, + const int ptex_face_index, + const float u, const float v, + float P[3]) +{ + BKE_subdiv_eval_limit_point_and_derivatives(subdiv, + ptex_face_index, + u, v, + P, NULL, NULL); +} + +void BKE_subdiv_eval_limit_point_and_derivatives( + Subdiv *subdiv, + const int ptex_face_index, + const float u, const float v, + float P[3], float dPdu[3], float dPdv[3]) +{ +#ifdef WITH_OPENSUBDIV + subdiv->evaluator->evaluateLimit(subdiv->evaluator, + ptex_face_index, + u, v, + P, dPdu, dPdv); +#else + UNUSED_VARS(subdiv, ptex_face_index, u, v, P, dPdu, dPdv); +#endif +} + +void BKE_subdiv_eval_limit_point_and_normal( + Subdiv *subdiv, + const int ptex_face_index, + const float u, const float v, + float P[3], float N[3]) +{ + float dPdu[3], dPdv[3]; + BKE_subdiv_eval_limit_point_and_derivatives(subdiv, + ptex_face_index, + u, v, + P, dPdu, dPdv); + cross_v3_v3v3(N, dPdu, dPdv); + normalize_v3(N); +} + +void BKE_subdiv_eval_limit_point_and_short_normal( + Subdiv *subdiv, + const int ptex_face_index, + const float u, const float v, + float P[3], short N[3]) +{ + float N_float[3]; + BKE_subdiv_eval_limit_point_and_normal(subdiv, + ptex_face_index, + u, v, + P, N_float); + normal_float_to_short_v3(N, N_float); +} + +void BKE_subdiv_eval_face_varying( + Subdiv *subdiv, + const int face_varying_channel, + const int ptex_face_index, + const float u, const float v, + float face_varying[2]) +{ +#ifdef WITH_OPENSUBDIV + subdiv->evaluator->evaluateFaceVarying(subdiv->evaluator, + face_varying_channel, + ptex_face_index, + u, v, + face_varying); +#else + UNUSED_VARS(subdiv, face_varying_channel, ptex_face_index, u, v, face_varying); +#endif +} + +/* =================== Patch queries at given resolution =================== */ + +/* Move buffer forward by a given number of bytes. */ +static void buffer_apply_offset(void **buffer, const int offset) +{ + *buffer = ((unsigned char *)*buffer) + offset; +} + +/* Write given number of floats to the beginning of given buffer. */ +static void buffer_write_float_value(void **buffer, + const float *values_buffer, int num_values) +{ + memcpy(*buffer, values_buffer, sizeof(float) * num_values); +} + +/* Similar to above, just operates with short values. */ +static void buffer_write_short_value(void **buffer, + const short *values_buffer, int num_values) +{ + memcpy(*buffer, values_buffer, sizeof(short) * num_values); +} + +void BKE_subdiv_eval_limit_patch_resolution_point( + Subdiv *subdiv, + const int ptex_face_index, + const int resolution, + void *buffer, const int offset, const int stride) +{ + buffer_apply_offset(&buffer, offset); + const float inv_resolution_1 = 1.0f / (float)(resolution - 1); + for (int y = 0; y < resolution; y++) { + const float v = y * inv_resolution_1; + for (int x = 0; x < resolution; x++) { + const float u = x * inv_resolution_1; + BKE_subdiv_eval_limit_point(subdiv, + ptex_face_index, + u, v, + buffer); + buffer_apply_offset(&buffer, stride); + } + } +} + +void BKE_subdiv_eval_limit_patch_resolution_point_and_derivatives( + Subdiv *subdiv, + const int ptex_face_index, + const int resolution, + void *point_buffer, const int point_offset, const int point_stride, + void *du_buffer, const int du_offset, const int du_stride, + void *dv_buffer, const int dv_offset, const int dv_stride) +{ + buffer_apply_offset(&point_buffer, point_offset); + buffer_apply_offset(&du_buffer, du_offset); + buffer_apply_offset(&dv_buffer, dv_offset); + const float inv_resolution_1 = 1.0f / (float)(resolution - 1); + for (int y = 0; y < resolution; y++) { + const float v = y * inv_resolution_1; + for (int x = 0; x < resolution; x++) { + const float u = x * inv_resolution_1; + BKE_subdiv_eval_limit_point_and_derivatives( + subdiv, + ptex_face_index, + u, v, + point_buffer, du_buffer, dv_buffer); + buffer_apply_offset(&point_buffer, point_stride); + buffer_apply_offset(&du_buffer, du_stride); + buffer_apply_offset(&dv_buffer, dv_stride); + } + } +} + +void BKE_subdiv_eval_limit_patch_resolution_point_and_normal( + Subdiv *subdiv, + const int ptex_face_index, + const int resolution, + void *point_buffer, const int point_offset, const int point_stride, + void *normal_buffer, const int normal_offset, const int normal_stride) +{ + buffer_apply_offset(&point_buffer, point_offset); + buffer_apply_offset(&normal_buffer, normal_offset); + const float inv_resolution_1 = 1.0f / (float)(resolution - 1); + for (int y = 0; y < resolution; y++) { + const float v = y * inv_resolution_1; + for (int x = 0; x < resolution; x++) { + const float u = x * inv_resolution_1; + float normal[3]; + BKE_subdiv_eval_limit_point_and_normal( + subdiv, + ptex_face_index, + u, v, + point_buffer, normal); + buffer_write_float_value(&normal_buffer, normal, 3); + buffer_apply_offset(&point_buffer, point_stride); + buffer_apply_offset(&normal_buffer, normal_stride); + } + } +} + +void BKE_subdiv_eval_limit_patch_resolution_point_and_short_normal( + Subdiv *subdiv, + const int ptex_face_index, + const int resolution, + void *point_buffer, const int point_offset, const int point_stride, + void *normal_buffer, const int normal_offset, const int normal_stride) +{ + buffer_apply_offset(&point_buffer, point_offset); + buffer_apply_offset(&normal_buffer, normal_offset); + const float inv_resolution_1 = 1.0f / (float)(resolution - 1); + for (int y = 0; y < resolution; y++) { + const float v = y * inv_resolution_1; + for (int x = 0; x < resolution; x++) { + const float u = x * inv_resolution_1; + short normal[3]; + BKE_subdiv_eval_limit_point_and_short_normal( + subdiv, + ptex_face_index, + u, v, + point_buffer, normal); + buffer_write_short_value(&normal_buffer, normal, 3); + buffer_apply_offset(&point_buffer, point_stride); + buffer_apply_offset(&normal_buffer, normal_stride); + } + } +} diff --git a/source/blender/blenkernel/intern/subdiv_mesh.c b/source/blender/blenkernel/intern/subdiv_mesh.c new file mode 100644 index 00000000000..bb27cb6a31e --- /dev/null +++ b/source/blender/blenkernel/intern/subdiv_mesh.c @@ -0,0 +1,2432 @@ +/* + * ***** 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. + * + * The Original Code is Copyright (C) 2018 by Blender Foundation. + * All rights reserved. + * + * Contributor(s): Sergey Sharybin. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/blenkernel/intern/subdiv_mesh.c + * \ingroup bke + */ + +#include "BKE_subdiv.h" + +#include "atomic_ops.h" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_key_types.h" + +#include "BLI_alloca.h" +#include "BLI_bitmap.h" +#include "BLI_math_vector.h" +#include "BLI_task.h" + +#include "BKE_mesh.h" +#include "BKE_key.h" + +#include "MEM_guardedalloc.h" + +/* ============================================================================= + * General helpers. + */ + +/* Number of ptex faces for a given polygon. */ +BLI_INLINE int num_ptex_faces_per_poly_get(const MPoly *poly) +{ + return (poly->totloop == 4) ? 1 : poly->totloop; +} + +BLI_INLINE int num_edges_per_ptex_face_get(const int resolution) +{ + return 2 * (resolution - 1) * resolution; +} + +BLI_INLINE int num_inner_edges_per_ptex_face_get(const int resolution) +{ + if (resolution < 2) { + return 0; + } + return (resolution - 2) * resolution + + (resolution - 1) * (resolution - 1); +} + +/* Number of subdivision polygons per ptex face. */ +BLI_INLINE int num_polys_per_ptex_get(const int resolution) +{ + return (resolution - 1) * (resolution - 1); +} + +/* Subdivision resolution per given polygon's ptex faces. */ +BLI_INLINE int ptex_face_resolution_get(const MPoly *poly, int resolution) +{ + return (poly->totloop == 4) ? (resolution) + : ((resolution >> 1) + 1); +} + +/* ============================================================================= + * Mesh subdivision context. + */ + +typedef struct SubdivMeshContext { + const Mesh *coarse_mesh; + Subdiv *subdiv; + Mesh *subdiv_mesh; + const SubdivToMeshSettings *settings; + /* Cached custom data arrays for fastter access. */ + int *vert_origindex; + int *edge_origindex; + int *loop_origindex; + int *poly_origindex; + /* UV layers interpolation. */ + int num_uv_layers; + MLoopUV *uv_layers[MAX_MTFACE]; + /* Counters of geometry in subdivided mesh, initialized as a part of + * offsets calculation. + */ + int num_subdiv_vertices; + int num_subdiv_edges; + int num_subdiv_loops; + int num_subdiv_polygons; + /* Offsets of various geometry in the subdivision mesh arrays. */ + int vertices_corner_offset; + int vertices_edge_offset; + int vertices_inner_offset; + int edge_boundary_offset; + int edge_inner_offset; + /* Indexed by coarse polygon index, indicates offset in subdivided mesh + * vertices, edges and polygons arrays, where first element of the poly + * begins. + */ + int *subdiv_vertex_offset; + int *subdiv_edge_offset; + int *subdiv_polygon_offset; + /* Indexed by base face index, element indicates total number of ptex faces + * created for preceding base faces. + */ + int *face_ptex_offset; + /* Bitmap indicating whether vertex was used already or not. + * - During patch evaluation indicates whether coarse vertex was already + * evaluated and its position on limit is already known. + */ + BLI_bitmap *coarse_vertices_used_map; + /* Bitmap indicating whether edge was used already or not. This includes: + * - During context initialization it indicates whether subdivided verticies + * for corresponding edge were already calculated or not. + * - During patch evaluation it indicates whether vertices along this edge + * were already evaluated. + */ + BLI_bitmap *coarse_edges_used_map; +} SubdivMeshContext; + +static void subdiv_mesh_ctx_cache_uv_layers(SubdivMeshContext *ctx) +{ + Mesh *subdiv_mesh = ctx->subdiv_mesh; + ctx->num_uv_layers = + CustomData_number_of_layers(&subdiv_mesh->ldata, CD_MLOOPUV); + for (int layer_index = 0; layer_index < ctx->num_uv_layers; ++layer_index) { + ctx->uv_layers[layer_index] = CustomData_get_layer_n( + &subdiv_mesh->ldata, CD_MLOOPUV, layer_index); + } +} + +static void subdiv_mesh_ctx_cache_custom_data_layers(SubdivMeshContext *ctx) +{ + Mesh *subdiv_mesh = ctx->subdiv_mesh; + /* Pointers to original indices layers. */ + ctx->vert_origindex = CustomData_get_layer( + &subdiv_mesh->vdata, CD_ORIGINDEX); + ctx->edge_origindex = CustomData_get_layer( + &subdiv_mesh->edata, CD_ORIGINDEX); + ctx->loop_origindex = CustomData_get_layer( + &subdiv_mesh->ldata, CD_ORIGINDEX); + ctx->poly_origindex = CustomData_get_layer( + &subdiv_mesh->pdata, CD_ORIGINDEX); + /* UV layers interpolation. */ + subdiv_mesh_ctx_cache_uv_layers(ctx); +} + +/* NOTE: Expects edge map to be zeroed. */ +static void subdiv_mesh_ctx_count(SubdivMeshContext *ctx) +{ + /* Reset counters. */ + ctx->num_subdiv_vertices = 0; + ctx->num_subdiv_edges = 0; + ctx->num_subdiv_loops = 0; + ctx->num_subdiv_polygons = 0; + /* Static geometry counters. */ + const int resolution = ctx->settings->resolution; + const int no_quad_patch_resolution = ((resolution >> 1) + 1); + const int num_subdiv_vertices_per_coarse_edge = resolution - 2; + const int num_inner_vertices_per_quad = (resolution - 2) * (resolution - 2); + const int num_inner_vertices_per_noquad_patch = + (no_quad_patch_resolution - 2) * (no_quad_patch_resolution - 2); + const Mesh *coarse_mesh = ctx->coarse_mesh; + const MLoop *coarse_mloop = coarse_mesh->mloop; + const MPoly *coarse_mpoly = coarse_mesh->mpoly; + ctx->num_subdiv_vertices = coarse_mesh->totvert; + ctx->num_subdiv_edges = + coarse_mesh->totedge * (num_subdiv_vertices_per_coarse_edge + 1); + /* Calculate extra vertices and edges createdd by non-loose geometry. */ + for (int poly_index = 0; poly_index < coarse_mesh->totpoly; poly_index++) { + const MPoly *coarse_poly = &coarse_mpoly[poly_index]; + const int num_ptex_faces_per_poly = + num_ptex_faces_per_poly_get(coarse_poly); + for (int corner = 0; corner < coarse_poly->totloop; corner++) { + const MLoop *loop = &coarse_mloop[coarse_poly->loopstart + corner]; + const bool is_edge_used = + BLI_BITMAP_TEST_BOOL(ctx->coarse_edges_used_map, loop->e); + /* Edges which aren't counted yet. */ + if (!is_edge_used) { + BLI_BITMAP_ENABLE(ctx->coarse_edges_used_map, loop->e); + ctx->num_subdiv_vertices += num_subdiv_vertices_per_coarse_edge; + } + } + /* Inner verticies of polygon. */ + if (num_ptex_faces_per_poly == 1) { + ctx->num_subdiv_vertices += num_inner_vertices_per_quad; + ctx->num_subdiv_edges += + num_edges_per_ptex_face_get(resolution - 2) + + 4 * num_subdiv_vertices_per_coarse_edge; + ctx->num_subdiv_polygons += num_polys_per_ptex_get(resolution); + } + else { + ctx->num_subdiv_vertices += + 1 + + num_ptex_faces_per_poly * (no_quad_patch_resolution - 2) + + num_ptex_faces_per_poly * num_inner_vertices_per_noquad_patch; + ctx->num_subdiv_edges += + num_ptex_faces_per_poly * + (num_inner_edges_per_ptex_face_get( + no_quad_patch_resolution - 1) + + (no_quad_patch_resolution - 2) + + num_subdiv_vertices_per_coarse_edge); + if (no_quad_patch_resolution >= 3) { + ctx->num_subdiv_edges += coarse_poly->totloop; + } + ctx->num_subdiv_polygons += + num_ptex_faces_per_poly * + num_polys_per_ptex_get(no_quad_patch_resolution); + } + } + /* Calculate extra edges createdd by loose edges. */ + for (int edge_index = 0; edge_index < coarse_mesh->totedge; edge_index++) { + if (!BLI_BITMAP_TEST_BOOL(ctx->coarse_edges_used_map, edge_index)) { + ctx->num_subdiv_vertices += num_subdiv_vertices_per_coarse_edge; + } + } + ctx->num_subdiv_loops = ctx->num_subdiv_polygons * 4; +} + +static void subdiv_mesh_ctx_init_offsets(SubdivMeshContext *ctx) +{ + const Mesh *coarse_mesh = ctx->coarse_mesh; + const int resolution = ctx->settings->resolution; + const int resolution_2 = resolution - 2; + const int resolution_2_squared = resolution_2 * resolution_2; + const int no_quad_patch_resolution = ((resolution >> 1) + 1); + const int num_irregular_vertices_per_patch = + (no_quad_patch_resolution - 2) * (no_quad_patch_resolution - 1); + const int num_subdiv_vertices_per_coarse_edge = resolution - 2; + const int num_subdiv_edges_per_coarse_edge = resolution - 1; + /* Constant offsets in arrays. */ + ctx->vertices_corner_offset = 0; + ctx->vertices_edge_offset = coarse_mesh->totvert; + ctx->vertices_inner_offset = + ctx->vertices_edge_offset + + coarse_mesh->totedge * num_subdiv_vertices_per_coarse_edge; + ctx->edge_boundary_offset = 0; + ctx->edge_inner_offset = + ctx->edge_boundary_offset + + coarse_mesh->totedge * num_subdiv_edges_per_coarse_edge; + /* "Indexed" offsets. */ + const MPoly *coarse_mpoly = coarse_mesh->mpoly; + int vertex_offset = 0; + int edge_offset = 0; + int polygon_offset = 0; + int face_ptex_offset = 0; + for (int poly_index = 0; poly_index < coarse_mesh->totpoly; poly_index++) { + const MPoly *coarse_poly = &coarse_mpoly[poly_index]; + const int num_ptex_faces_per_poly = + num_ptex_faces_per_poly_get(coarse_poly); + ctx->face_ptex_offset[poly_index] = face_ptex_offset; + ctx->subdiv_vertex_offset[poly_index] = vertex_offset; + ctx->subdiv_edge_offset[poly_index] = edge_offset; + ctx->subdiv_polygon_offset[poly_index] = polygon_offset; + face_ptex_offset += num_ptex_faces_per_poly; + if (num_ptex_faces_per_poly == 1) { + vertex_offset += resolution_2_squared; + edge_offset += num_edges_per_ptex_face_get(resolution - 2) + + 4 * num_subdiv_vertices_per_coarse_edge; + polygon_offset += num_polys_per_ptex_get(resolution); + } + else { + vertex_offset += + 1 + + num_ptex_faces_per_poly * num_irregular_vertices_per_patch; + edge_offset += + num_ptex_faces_per_poly * + (num_inner_edges_per_ptex_face_get( + no_quad_patch_resolution - 1) + + (no_quad_patch_resolution - 2) + + num_subdiv_vertices_per_coarse_edge); + if (no_quad_patch_resolution >= 3) { + edge_offset += coarse_poly->totloop; + } + polygon_offset += + num_ptex_faces_per_poly * + num_polys_per_ptex_get(no_quad_patch_resolution); + } + } +} + +static void subdiv_mesh_ctx_init(SubdivMeshContext *ctx) +{ + const Mesh *coarse_mesh = ctx->coarse_mesh; + /* Allocate maps and offsets. */ + ctx->coarse_vertices_used_map = + BLI_BITMAP_NEW(coarse_mesh->totvert, "vertices used map"); + ctx->coarse_edges_used_map = + BLI_BITMAP_NEW(coarse_mesh->totedge, "edges used map"); + ctx->subdiv_vertex_offset = MEM_malloc_arrayN( + coarse_mesh->totpoly, + sizeof(*ctx->subdiv_vertex_offset), + "vertex_offset"); + ctx->subdiv_edge_offset = MEM_malloc_arrayN( + coarse_mesh->totpoly, + sizeof(*ctx->subdiv_edge_offset), + "subdiv_edge_offset"); + ctx->subdiv_polygon_offset = MEM_malloc_arrayN( + coarse_mesh->totpoly, + sizeof(*ctx->subdiv_polygon_offset), + "subdiv_edge_offset"); + ctx->face_ptex_offset = MEM_malloc_arrayN(coarse_mesh->totpoly, + sizeof(*ctx->face_ptex_offset), + "face_ptex_offset"); + /* Initialize all offsets. */ + subdiv_mesh_ctx_init_offsets(ctx); + /* Calculate number of geometry in the result subdivision mesh. */ + subdiv_mesh_ctx_count(ctx); + /* Re-set maps which were used at this step. */ + BLI_BITMAP_SET_ALL(ctx->coarse_edges_used_map, false, coarse_mesh->totedge); +} + +static void subdiv_mesh_ctx_init_result(SubdivMeshContext *ctx) +{ + subdiv_mesh_ctx_cache_custom_data_layers(ctx); +} + +static void subdiv_mesh_ctx_free(SubdivMeshContext *ctx) +{ + MEM_freeN(ctx->coarse_vertices_used_map); + MEM_freeN(ctx->coarse_edges_used_map); + MEM_freeN(ctx->subdiv_vertex_offset); + MEM_freeN(ctx->subdiv_edge_offset); + MEM_freeN(ctx->subdiv_polygon_offset); + MEM_freeN(ctx->face_ptex_offset); +} + +/* ============================================================================= + * Loop custom data copy helpers. + */ + +typedef struct LoopsOfPtex { + /* First loop of the ptex, starts at ptex (0, 0) and goes in u direction. */ + const MLoop *first_loop; + /* Last loop of the ptex, starts at ptex (0, 0) and goes in v direction. */ + const MLoop *last_loop; + /* For quad coarse faces only. */ + const MLoop *second_loop; + const MLoop *third_loop; +} LoopsOfPtex; + +static void loops_of_ptex_get( + const SubdivMeshContext *ctx, + LoopsOfPtex *loops_of_ptex, + const MPoly *coarse_poly, + const int ptex_of_poly_index) +{ + const MLoop *coarse_mloop = ctx->coarse_mesh->mloop; + const int first_ptex_loop_index = + coarse_poly->loopstart + ptex_of_poly_index; + /* Loop which look in the (opposite) V direction of the current + * ptex face. + * + * TOOD(sergey): Get rid of using module on every iteration. + */ + const int last_ptex_loop_index = + coarse_poly->loopstart + + (ptex_of_poly_index + coarse_poly->totloop - 1) % + coarse_poly->totloop; + loops_of_ptex->first_loop = &coarse_mloop[first_ptex_loop_index]; + loops_of_ptex->last_loop = &coarse_mloop[last_ptex_loop_index]; + if (coarse_poly->totloop == 4) { + loops_of_ptex->second_loop = loops_of_ptex->first_loop + 1; + loops_of_ptex->third_loop = loops_of_ptex->first_loop + 2; + } + else { + loops_of_ptex->second_loop = NULL; + loops_of_ptex->third_loop = NULL; + } +} + +/* ============================================================================= + * Vertex custom data interpolation helpers. + */ + +/* TODO(sergey): Somehow de-duplicate with loops storage, without too much + * exception cases all over the code. + */ + +typedef struct VerticesForInterpolation { + /* This field points to a vertex data which is to be used for interpolation. + * The idea is to avoid unnecessary allocations for regular faces, where + * we can simply + */ + const CustomData *vertex_data; + /* Vertices data calculated for ptex corners. There are always 4 elements + * in this custom data, aligned the following way: + * + * index 0 -> uv (0, 0) + * index 1 -> uv (0, 1) + * index 2 -> uv (1, 1) + * index 3 -> uv (1, 0) + * + * Is allocated for non-regular faces (triangles and n-gons). + */ + CustomData vertex_data_storage; + bool vertex_data_storage_allocated; + /* Infices within vertex_data to interpolate for. The indices are aligned + * with uv coordinates in a similar way as indices in loop_data_storage. + */ + int vertex_indices[4]; +} VerticesForInterpolation; + +static void vertex_interpolation_init( + const SubdivMeshContext *ctx, + VerticesForInterpolation *vertex_interpolation, + const MPoly *coarse_poly) +{ + const Mesh *coarse_mesh = ctx->coarse_mesh; + const MLoop *coarse_mloop = coarse_mesh->mloop; + if (coarse_poly->totloop == 4) { + vertex_interpolation->vertex_data = &coarse_mesh->vdata; + vertex_interpolation->vertex_indices[0] = + coarse_mloop[coarse_poly->loopstart + 0].v; + vertex_interpolation->vertex_indices[1] = + coarse_mloop[coarse_poly->loopstart + 1].v; + vertex_interpolation->vertex_indices[2] = + coarse_mloop[coarse_poly->loopstart + 2].v; + vertex_interpolation->vertex_indices[3] = + coarse_mloop[coarse_poly->loopstart + 3].v; + vertex_interpolation->vertex_data_storage_allocated = false; + } + else { + vertex_interpolation->vertex_data = + &vertex_interpolation->vertex_data_storage; + /* Allocate storage for loops corresponding to ptex corners. */ + CustomData_copy(&ctx->coarse_mesh->vdata, + &vertex_interpolation->vertex_data_storage, + CD_MASK_EVERYTHING, + CD_CALLOC, + 4); + /* Initialize indices. */ + vertex_interpolation->vertex_indices[0] = 0; + vertex_interpolation->vertex_indices[1] = 1; + vertex_interpolation->vertex_indices[2] = 2; + vertex_interpolation->vertex_indices[3] = 3; + vertex_interpolation->vertex_data_storage_allocated = true; + /* Interpolate center of poly right away, it stays unchanged for all + * ptex faces. + */ + const float weight = 1.0f / (float)coarse_poly->totloop; + float *weights = BLI_array_alloca(weights, coarse_poly->totloop); + int *indices = BLI_array_alloca(indices, coarse_poly->totloop); + for (int i = 0; i < coarse_poly->totloop; ++i) { + weights[i] = weight; + indices[i] = coarse_mloop[coarse_poly->loopstart + i].v; + } + CustomData_interp(&coarse_mesh->vdata, + &vertex_interpolation->vertex_data_storage, + indices, + weights, NULL, + coarse_poly->totloop, + 2); + } +} + +static void vertex_interpolation_from_ptex( + const SubdivMeshContext *ctx, + VerticesForInterpolation *vertex_interpolation, + const MPoly *coarse_poly, + const int ptex_of_poly_index) +{ + if (coarse_poly->totloop == 4) { + /* Nothing to do, all indices and data is already assigned. */ + } + else { + const CustomData *vertex_data = &ctx->coarse_mesh->vdata; + const Mesh *coarse_mesh = ctx->coarse_mesh; + const MLoop *coarse_mloop = coarse_mesh->mloop; + LoopsOfPtex loops_of_ptex; + loops_of_ptex_get(ctx, &loops_of_ptex, coarse_poly, ptex_of_poly_index); + /* Ptex face corner corresponds to a poly loop with same index. */ + CustomData_copy_data( + vertex_data, + &vertex_interpolation->vertex_data_storage, + coarse_mloop[coarse_poly->loopstart + ptex_of_poly_index].v, + 0, + 1); + /* Interpolate remaining ptex face corners, which hits loops + * middle points. + * + * TODO(sergey): Re-use one of interpolation results from previous + * iteration. + */ + const float weights[2] = {0.5f, 0.5f}; + const int first_loop_index = loops_of_ptex.first_loop - coarse_mloop; + const int last_loop_index = loops_of_ptex.last_loop - coarse_mloop; + const int first_indices[2] = { + coarse_mloop[first_loop_index].v, + coarse_mloop[coarse_poly->loopstart + + (first_loop_index - coarse_poly->loopstart + 1) % + coarse_poly->totloop].v}; + const int last_indices[2] = {coarse_mloop[first_loop_index].v, + coarse_mloop[last_loop_index].v}; + CustomData_interp(vertex_data, + &vertex_interpolation->vertex_data_storage, + first_indices, + weights, NULL, + 2, + 1); + CustomData_interp(vertex_data, + &vertex_interpolation->vertex_data_storage, + last_indices, + weights, NULL, + 2, + 3); + } +} + +static void vertex_interpolation_end( + VerticesForInterpolation *vertex_interpolation) +{ + if (vertex_interpolation->vertex_data_storage_allocated) { + CustomData_free(&vertex_interpolation->vertex_data_storage, 4); + } +} + +/* ============================================================================= + * Loop custom data interpolation helpers. + */ + +typedef struct LoopsForInterpolation { + /* This field points to a loop data which is to be used for interpolation. + * The idea is to avoid unnecessary allocations for regular faces, where + * we can simply + */ + const CustomData *loop_data; + /* Loops data calculated for ptex corners. There are always 4 elements + * in this custom data, aligned the following way: + * + * index 0 -> uv (0, 0) + * index 1 -> uv (0, 1) + * index 2 -> uv (1, 1) + * index 3 -> uv (1, 0) + * + * Is allocated for non-regular faces (triangles and n-gons). + */ + CustomData loop_data_storage; + bool loop_data_storage_allocated; + /* Infices within loop_data to interpolate for. The indices are aligned with + * uv coordinates in a similar way as indices in loop_data_storage. + */ + int loop_indices[4]; +} LoopsForInterpolation; + +static void loop_interpolation_init( + const SubdivMeshContext *ctx, + LoopsForInterpolation *loop_interpolation, + const MPoly *coarse_poly) +{ + const Mesh *coarse_mesh = ctx->coarse_mesh; + if (coarse_poly->totloop == 4) { + loop_interpolation->loop_data = &coarse_mesh->ldata; + loop_interpolation->loop_indices[0] = coarse_poly->loopstart + 0; + loop_interpolation->loop_indices[1] = coarse_poly->loopstart + 1; + loop_interpolation->loop_indices[2] = coarse_poly->loopstart + 2; + loop_interpolation->loop_indices[3] = coarse_poly->loopstart + 3; + loop_interpolation->loop_data_storage_allocated = false; + } + else { + loop_interpolation->loop_data = &loop_interpolation->loop_data_storage; + /* Allocate storage for loops corresponding to ptex corners. */ + CustomData_copy(&ctx->coarse_mesh->ldata, + &loop_interpolation->loop_data_storage, + CD_MASK_EVERYTHING, + CD_CALLOC, + 4); + /* Initialize indices. */ + loop_interpolation->loop_indices[0] = 0; + loop_interpolation->loop_indices[1] = 1; + loop_interpolation->loop_indices[2] = 2; + loop_interpolation->loop_indices[3] = 3; + loop_interpolation->loop_data_storage_allocated = true; + /* Interpolate center of poly right away, it stays unchanged for all + * ptex faces. + */ + const float weight = 1.0f / (float)coarse_poly->totloop; + float *weights = BLI_array_alloca(weights, coarse_poly->totloop); + int *indices = BLI_array_alloca(indices, coarse_poly->totloop); + for (int i = 0; i < coarse_poly->totloop; ++i) { + weights[i] = weight; + indices[i] = coarse_poly->loopstart + i; + } + CustomData_interp(&coarse_mesh->ldata, + &loop_interpolation->loop_data_storage, + indices, + weights, NULL, + coarse_poly->totloop, + 2); + } +} + +static void loop_interpolation_from_ptex( + const SubdivMeshContext *ctx, + LoopsForInterpolation *loop_interpolation, + const MPoly *coarse_poly, + const int ptex_face_index) +{ + if (coarse_poly->totloop == 4) { + /* Nothing to do, all indices and data is already assigned. */ + } + else { + const CustomData *loop_data = &ctx->coarse_mesh->ldata; + const Mesh *coarse_mesh = ctx->coarse_mesh; + const MLoop *coarse_mloop = coarse_mesh->mloop; + LoopsOfPtex loops_of_ptex; + loops_of_ptex_get(ctx, &loops_of_ptex, coarse_poly, ptex_face_index); + /* Ptex face corner corresponds to a poly loop with same index. */ + CustomData_copy_data(loop_data, + &loop_interpolation->loop_data_storage, + coarse_poly->loopstart + ptex_face_index, + 0, + 1); + /* Interpolate remaining ptex face corners, which hits loops + * middle points. + * + * TODO(sergey): Re-use one of interpolation results from previous + * iteration. + */ + const float weights[2] = {0.5f, 0.5f}; + const int first_indices[2] = { + loops_of_ptex.first_loop - coarse_mloop, + (loops_of_ptex.first_loop + 1 - coarse_mloop) % + coarse_poly->totloop}; + const int last_indices[2] = { + loops_of_ptex.last_loop - coarse_mloop, + loops_of_ptex.first_loop - coarse_mloop}; + CustomData_interp(loop_data, + &loop_interpolation->loop_data_storage, + first_indices, + weights, NULL, + 2, + 1); + CustomData_interp(loop_data, + &loop_interpolation->loop_data_storage, + last_indices, + weights, NULL, + 2, + 3); + } +} + +static void loop_interpolation_end(LoopsForInterpolation *loop_interpolation) +{ + if (loop_interpolation->loop_data_storage_allocated) { + CustomData_free(&loop_interpolation->loop_data_storage, 4); + } +} + +/* ============================================================================= + * Vertex subdivision process. + */ + +/* Custom data interpolation helpers. */ + +static void subdiv_vertex_data_copy( + const SubdivMeshContext *ctx, + const MVert *coarse_vertex, + MVert *subdiv_vertex) +{ + const Mesh *coarse_mesh = ctx->coarse_mesh; + Mesh *subdiv_mesh = ctx->subdiv_mesh; + const int coarse_vertex_index = coarse_vertex - coarse_mesh->mvert; + const int subdiv_vertex_index = subdiv_vertex - subdiv_mesh->mvert; + CustomData_copy_data(&coarse_mesh->vdata, + &ctx->subdiv_mesh->vdata, + coarse_vertex_index, + subdiv_vertex_index, + 1); +} + +static void subdiv_vertex_data_interpolate( + const SubdivMeshContext *ctx, + MVert *subdiv_vertex, + const VerticesForInterpolation *vertex_interpolation, + const float u, const float v) +{ + const int subdiv_vertex_index = subdiv_vertex - ctx->subdiv_mesh->mvert; + const float weights[4] = {(1.0f - u) * (1.0f - v), + u * (1.0f - v), + u * v, + (1.0f - u) * v}; + CustomData_interp(vertex_interpolation->vertex_data, + &ctx->subdiv_mesh->vdata, + vertex_interpolation->vertex_indices, + weights, NULL, + 4, + subdiv_vertex_index); + if (ctx->vert_origindex != NULL) { + ctx->vert_origindex[subdiv_vertex_index] = ORIGINDEX_NONE; + } +} + +/* Evaluation of corner vertices. They are coming from coarse vertices. */ + +static void subdiv_evaluate_corner_vertices_regular( + SubdivMeshContext *ctx, + const MPoly *coarse_poly) +{ + const float weights[4][2] = {{0.0f, 0.0f}, + {1.0f, 0.0f}, + {1.0f, 1.0f}, + {0.0f, 1.0f}}; + Subdiv *subdiv = ctx->subdiv; + const Mesh *coarse_mesh = ctx->coarse_mesh; + const MVert *coarse_mvert = coarse_mesh->mvert; + const MLoop *coarse_mloop = coarse_mesh->mloop; + Mesh *subdiv_mesh = ctx->subdiv_mesh; + MVert *subdiv_mvert = subdiv_mesh->mvert; + const int poly_index = coarse_poly - coarse_mesh->mpoly; + const int ptex_face_index = ctx->face_ptex_offset[poly_index]; + for (int corner = 0; corner < coarse_poly->totloop; corner++) { + const MLoop *coarse_loop = + &coarse_mloop[coarse_poly->loopstart + corner]; + if (BLI_BITMAP_TEST_AND_SET_ATOMIC(ctx->coarse_vertices_used_map, + coarse_loop->v)) + { + continue; + } + const MVert *coarse_vert = &coarse_mvert[coarse_loop->v]; + MVert *subdiv_vert = &subdiv_mvert[ + ctx->vertices_corner_offset + coarse_loop->v]; + subdiv_vertex_data_copy(ctx, coarse_vert, subdiv_vert); + BKE_subdiv_eval_limit_point_and_short_normal( + subdiv, + ptex_face_index, + weights[corner][0], weights[corner][1], + subdiv_vert->co, subdiv_vert->no); + } +} + +static void subdiv_evaluate_corner_vertices_special( + SubdivMeshContext *ctx, + const MPoly *coarse_poly) +{ + Subdiv *subdiv = ctx->subdiv; + const Mesh *coarse_mesh = ctx->coarse_mesh; + const MVert *coarse_mvert = coarse_mesh->mvert; + const MLoop *coarse_mloop = coarse_mesh->mloop; + Mesh *subdiv_mesh = ctx->subdiv_mesh; + MVert *subdiv_mvert = subdiv_mesh->mvert; + const int poly_index = coarse_poly - coarse_mesh->mpoly; + int ptex_face_index = ctx->face_ptex_offset[poly_index]; + for (int corner = 0; + corner < coarse_poly->totloop; + corner++, ptex_face_index++) + { + const MLoop *coarse_loop = + &coarse_mloop[coarse_poly->loopstart + corner]; + if (BLI_BITMAP_TEST_AND_SET_ATOMIC(ctx->coarse_vertices_used_map, + coarse_loop->v)) + { + continue; + } + const MVert *coarse_vert = &coarse_mvert[coarse_loop->v]; + MVert *subdiv_vert = &subdiv_mvert[ + ctx->vertices_corner_offset + coarse_loop->v]; + subdiv_vertex_data_copy(ctx, coarse_vert, subdiv_vert); + BKE_subdiv_eval_limit_point_and_short_normal( + subdiv, + ptex_face_index, + 0.0f, 0.0f, + subdiv_vert->co, subdiv_vert->no); + } +} + +static void subdiv_evaluate_corner_vertices(SubdivMeshContext *ctx, + const MPoly *coarse_poly) +{ + if (coarse_poly->totloop == 4) { + subdiv_evaluate_corner_vertices_regular(ctx, coarse_poly); + } + else { + subdiv_evaluate_corner_vertices_special(ctx, coarse_poly); + } +} + +/* Evaluation of edge vertices. They are coming from coarse edges. */ + +static void subdiv_evaluate_edge_vertices_regular( + SubdivMeshContext *ctx, + const MPoly *coarse_poly, + VerticesForInterpolation *vertex_interpolation) +{ + const int resolution = ctx->settings->resolution; + const int resolution_1 = resolution - 1; + const float inv_resolution_1 = 1.0f / (float)resolution_1; + const int num_subdiv_vertices_per_coarse_edge = resolution - 2; + Subdiv *subdiv = ctx->subdiv; + const Mesh *coarse_mesh = ctx->coarse_mesh; + const MEdge *coarse_medge = coarse_mesh->medge; + const MLoop *coarse_mloop = coarse_mesh->mloop; + Mesh *subdiv_mesh = ctx->subdiv_mesh; + MVert *subdiv_mvert = subdiv_mesh->mvert; + const int poly_index = coarse_poly - coarse_mesh->mpoly; + const int ptex_face_index = ctx->face_ptex_offset[poly_index]; + for (int corner = 0; corner < coarse_poly->totloop; corner++) { + const MLoop *coarse_loop = + &coarse_mloop[coarse_poly->loopstart + corner]; + if (BLI_BITMAP_TEST_AND_SET_ATOMIC(ctx->coarse_edges_used_map, + coarse_loop->e)) + { + continue; + } + vertex_interpolation_from_ptex(ctx, + vertex_interpolation, + coarse_poly, + corner); + const MEdge *coarse_edge = &coarse_medge[coarse_loop->e]; + const bool flip = (coarse_edge->v2 == coarse_loop->v); + MVert *subdiv_vert = &subdiv_mvert[ + ctx->vertices_edge_offset + + coarse_loop->e * num_subdiv_vertices_per_coarse_edge]; + for (int vertex_index = 0; + vertex_index < num_subdiv_vertices_per_coarse_edge; + vertex_index++, subdiv_vert++) + { + float fac = (vertex_index + 1) * inv_resolution_1; + if (flip) { + fac = 1.0f - fac; + } + if (corner >= 2) { + fac = 1.0f - fac; + } + float u, v; + if ((corner & 1) == 0) { + u = fac; + v = (corner == 2) ? 1.0f : 0.0f; + } + else { + u = (corner == 1) ? 1.0f : 0.0f; + v = fac; + } + subdiv_vertex_data_interpolate(ctx, + subdiv_vert, + vertex_interpolation, + u, v); + BKE_subdiv_eval_limit_point_and_short_normal( + subdiv, + ptex_face_index, + u, v, + subdiv_vert->co, subdiv_vert->no); + } + } +} + +static void subdiv_evaluate_edge_vertices_special( + SubdivMeshContext *ctx, + const MPoly *coarse_poly, + VerticesForInterpolation *vertex_interpolation) +{ + const int resolution = ctx->settings->resolution; + const int num_subdiv_vertices_per_coarse_edge = resolution - 2; + const int num_vertices_per_ptex_edge = ((resolution >> 1) + 1); + const float inv_ptex_resolution_1 = + 1.0f / (float)(num_vertices_per_ptex_edge - 1); + Subdiv *subdiv = ctx->subdiv; + const Mesh *coarse_mesh = ctx->coarse_mesh; + const MEdge *coarse_medge = coarse_mesh->medge; + const MLoop *coarse_mloop = coarse_mesh->mloop; + Mesh *subdiv_mesh = ctx->subdiv_mesh; + MVert *subdiv_mvert = subdiv_mesh->mvert; + const int poly_index = coarse_poly - coarse_mesh->mpoly; + const int ptex_face_start_index = ctx->face_ptex_offset[poly_index]; + int ptex_face_index = ptex_face_start_index; + for (int corner = 0; + corner < coarse_poly->totloop; + corner++, ptex_face_index++) + { + const MLoop *coarse_loop = + &coarse_mloop[coarse_poly->loopstart + corner]; + if (BLI_BITMAP_TEST_AND_SET_ATOMIC(ctx->coarse_edges_used_map, + coarse_loop->e)) + { + continue; + } + vertex_interpolation_from_ptex(ctx, + vertex_interpolation, + coarse_poly, + corner); + const MEdge *coarse_edge = &coarse_medge[coarse_loop->e]; + const bool flip = (coarse_edge->v2 == coarse_loop->v); + MVert *subdiv_vert = &subdiv_mvert[ + ctx->vertices_edge_offset + + coarse_loop->e * num_subdiv_vertices_per_coarse_edge]; + int veretx_delta = 1; + if (flip) { + subdiv_vert += num_subdiv_vertices_per_coarse_edge - 1; + veretx_delta = -1; + } + for (int vertex_index = 1; + vertex_index < num_vertices_per_ptex_edge; + vertex_index++, subdiv_vert += veretx_delta) + { + float u = vertex_index * inv_ptex_resolution_1; + subdiv_vertex_data_interpolate(ctx, + subdiv_vert, + vertex_interpolation, + u, 0.0f); + BKE_subdiv_eval_limit_point_and_short_normal( + subdiv, + ptex_face_index, + u, 0.0f, + subdiv_vert->co, subdiv_vert->no); + } + const int next_ptex_face_index = + ptex_face_start_index + (corner + 1) % coarse_poly->totloop; + for (int vertex_index = 1; + vertex_index < num_vertices_per_ptex_edge - 1; + vertex_index++, subdiv_vert += veretx_delta) + { + float v = 1.0f - vertex_index * inv_ptex_resolution_1; + subdiv_vertex_data_interpolate(ctx, + subdiv_vert, + vertex_interpolation, + 0.0f, v); + BKE_subdiv_eval_limit_point_and_short_normal( + subdiv, + next_ptex_face_index, + 0.0f, v, + subdiv_vert->co, subdiv_vert->no); + } + } +} + +static void subdiv_evaluate_edge_vertices( + SubdivMeshContext *ctx, + const MPoly *coarse_poly, + VerticesForInterpolation *vertex_interpolation) +{ + if (coarse_poly->totloop == 4) { + subdiv_evaluate_edge_vertices_regular( + ctx, coarse_poly, vertex_interpolation); + } + else { + subdiv_evaluate_edge_vertices_special( + ctx, coarse_poly, vertex_interpolation); + } +} + +/* Evaluation of inner vertices, they are coming from ptex patches. */ + +static void subdiv_evaluate_inner_vertices_regular( + SubdivMeshContext *ctx, + const MPoly *coarse_poly, + VerticesForInterpolation *vertex_interpolation) +{ + const int resolution = ctx->settings->resolution; + const float inv_resolution_1 = 1.0f / (float)(resolution - 1); + Subdiv *subdiv = ctx->subdiv; + const Mesh *coarse_mesh = ctx->coarse_mesh; + Mesh *subdiv_mesh = ctx->subdiv_mesh; + MVert *subdiv_mvert = subdiv_mesh->mvert; + const int poly_index = coarse_poly - coarse_mesh->mpoly; + const int ptex_face_index = ctx->face_ptex_offset[poly_index]; + const int start_vertex_index = ctx->subdiv_vertex_offset[poly_index]; + MVert *subdiv_vert = + &subdiv_mvert[ctx->vertices_inner_offset + start_vertex_index]; + vertex_interpolation_from_ptex(ctx, + vertex_interpolation, + coarse_poly, + 0); + for (int y = 1; y < resolution - 1; y++) { + const float v = y * inv_resolution_1; + for (int x = 1; x < resolution - 1; x++, subdiv_vert++) { + const float u = x * inv_resolution_1; + subdiv_vertex_data_interpolate(ctx, + subdiv_vert, + vertex_interpolation, + u, v); + BKE_subdiv_eval_limit_point_and_short_normal( + subdiv, + ptex_face_index, + u, v, + subdiv_vert->co, subdiv_vert->no); + } + } +} + +static void subdiv_evaluate_inner_vertices_special( + SubdivMeshContext *ctx, + const MPoly *coarse_poly, + VerticesForInterpolation *vertex_interpolation) +{ + const int resolution = ctx->settings->resolution; + const int ptex_face_resolution = ptex_face_resolution_get( + coarse_poly, resolution); + const float inv_ptex_face_resolution_1 = + 1.0f / (float)(ptex_face_resolution - 1); + Subdiv *subdiv = ctx->subdiv; + const Mesh *coarse_mesh = ctx->coarse_mesh; + Mesh *subdiv_mesh = ctx->subdiv_mesh; + MVert *subdiv_mvert = subdiv_mesh->mvert; + const int poly_index = coarse_poly - coarse_mesh->mpoly; + int ptex_face_index = ctx->face_ptex_offset[poly_index]; + const int start_vertex_index = ctx->subdiv_vertex_offset[poly_index]; + MVert *subdiv_vert = + &subdiv_mvert[ctx->vertices_inner_offset + start_vertex_index]; + vertex_interpolation_from_ptex(ctx, + vertex_interpolation, + coarse_poly, + 0); + subdiv_vertex_data_interpolate(ctx, + subdiv_vert, + vertex_interpolation, + 1.0f, 1.0f); + BKE_subdiv_eval_limit_point_and_short_normal( + subdiv, + ptex_face_index, + 1.0f, 1.0f, + subdiv_vert->co, subdiv_vert->no); + subdiv_vert++; + for (int corner = 0; + corner < coarse_poly->totloop; + corner++, ptex_face_index++) + { + if (corner != 0) { + vertex_interpolation_from_ptex(ctx, + vertex_interpolation, + coarse_poly, + corner); + } + for (int y = 1; y < ptex_face_resolution - 1; y++) { + const float v = y * inv_ptex_face_resolution_1; + for (int x = 1; x < ptex_face_resolution; x++, subdiv_vert++) { + const float u = x * inv_ptex_face_resolution_1; + subdiv_vertex_data_interpolate(ctx, + subdiv_vert, + vertex_interpolation, + u, v); + BKE_subdiv_eval_limit_point_and_short_normal( + subdiv, + ptex_face_index, + u, v, + subdiv_vert->co, subdiv_vert->no); + } + } + } +} + +static void subdiv_evaluate_inner_vertices( + SubdivMeshContext *ctx, + const MPoly *coarse_poly, + VerticesForInterpolation *vertex_interpolation) +{ + if (coarse_poly->totloop == 4) { + subdiv_evaluate_inner_vertices_regular( + ctx, coarse_poly, vertex_interpolation); + } + else { + subdiv_evaluate_inner_vertices_special( + ctx, coarse_poly, vertex_interpolation); + } +} + +/* Evaluate all vertices which are emitted from given coarse polygon. */ +static void subdiv_evaluate_vertices(SubdivMeshContext *ctx, + const int poly_index) +{ + /* Base/coarse mesh information. */ + const Mesh *coarse_mesh = ctx->coarse_mesh; + const MPoly *coarse_mpoly = coarse_mesh->mpoly; + const MPoly *coarse_poly = &coarse_mpoly[poly_index]; + /* Initialize vertex interpolation, it is reused by corner vertices, coarse + * edges and patch evaluation. + */ + VerticesForInterpolation vertex_interpolation; + vertex_interpolation_init(ctx, &vertex_interpolation, coarse_poly); + (void) vertex_interpolation; + subdiv_evaluate_corner_vertices(ctx, coarse_poly); + subdiv_evaluate_edge_vertices(ctx, coarse_poly, &vertex_interpolation); + subdiv_evaluate_inner_vertices(ctx, coarse_poly, &vertex_interpolation); + vertex_interpolation_end(&vertex_interpolation); +} + +/* ============================================================================= + * Edge subdivision process. + */ + +static void subdiv_copy_edge_data( + SubdivMeshContext *ctx, + MEdge *subdiv_edge, + const MEdge *coarse_edge) +{ + const int subdiv_edge_index = subdiv_edge - ctx->subdiv_mesh->medge; + if (coarse_edge == NULL) { + subdiv_edge->crease = 0; + subdiv_edge->bweight = 0; + subdiv_edge->flag = 0; + if (ctx->edge_origindex != NULL) { + ctx->edge_origindex[subdiv_edge_index] = ORIGINDEX_NONE; + } + return; + } + const int coarse_edge_index = coarse_edge - ctx->coarse_mesh->medge; + CustomData_copy_data(&ctx->coarse_mesh->edata, + &ctx->subdiv_mesh->edata, + coarse_edge_index, + subdiv_edge_index, + 1); +} + +static MEdge *subdiv_create_edges_row(SubdivMeshContext *ctx, + MEdge *subdiv_edge, + const MEdge *coarse_edge, + const int start_vertex_index, + const int num_edges_per_row) +{ + int vertex_index = start_vertex_index; + for (int edge_index = 0; + edge_index < num_edges_per_row - 1; + edge_index++, subdiv_edge++) + { + subdiv_copy_edge_data(ctx, subdiv_edge, coarse_edge); + subdiv_edge->v1 = vertex_index; + subdiv_edge->v2 = vertex_index + 1; + vertex_index += 1; + } + return subdiv_edge; +} + +static MEdge *subdiv_create_edges_column(SubdivMeshContext *ctx, + MEdge *subdiv_edge, + const MEdge *coarse_start_edge, + const MEdge *coarse_end_edge, + const int start_vertex_index, + const int num_edges_per_row) +{ + int vertex_index = start_vertex_index; + for (int edge_index = 0; + edge_index < num_edges_per_row; + edge_index++, subdiv_edge++) + { + const MEdge *coarse_edge = NULL; + if (edge_index == 0) { + coarse_edge = coarse_start_edge; + } + else if (edge_index == num_edges_per_row - 1) { + coarse_edge = coarse_end_edge; + } + subdiv_copy_edge_data(ctx, subdiv_edge, coarse_edge); + subdiv_edge->v1 = vertex_index; + subdiv_edge->v2 = vertex_index + num_edges_per_row; + vertex_index += 1; + } + return subdiv_edge; +} + +/* Create edges between inner vertices of patch, and also edges to the + * boundary. + */ + +/* Consider a subdivision of base face at level 1: + * + * y + * ^ + * | (6) ---- (7) ---- (8) + * | | | | + * | (3) ---- (4) ---- (5) + * | | | | + * | (0) ---- (1) ---- (2) + * o---------------------------> x + * + * This is illustrate which parts of geometry is created by code below. + */ + +static void subdiv_create_edges_all_patches_regular( + SubdivMeshContext *ctx, + const MPoly *coarse_poly) +{ + const Mesh *coarse_mesh = ctx->coarse_mesh; + const MEdge *coarse_medge = coarse_mesh->medge; + const MLoop *coarse_mloop = coarse_mesh->mloop; + const MPoly *coarse_mpoly = coarse_mesh->mpoly; + const int poly_index = coarse_poly - coarse_mpoly; + const int resolution = ctx->settings->resolution; + const int start_vertex_index = + ctx->vertices_inner_offset + + ctx->subdiv_vertex_offset[poly_index]; + const int num_subdiv_vertices_per_coarse_edge = resolution - 2; + Mesh *subdiv_mesh = ctx->subdiv_mesh; + MEdge *subdiv_medge = subdiv_mesh->medge; + MEdge *subdiv_edge = &subdiv_medge[ + ctx->edge_inner_offset + ctx->subdiv_edge_offset[poly_index]]; + /* Create bottom row of edges (0-1, 1-2). */ + subdiv_edge = subdiv_create_edges_row( + ctx, + subdiv_edge, + NULL, + start_vertex_index, + resolution - 2); + /* Create remaining edges. */ + for (int row = 0; row < resolution - 3; row++) { + const int start_row_vertex_index = + start_vertex_index + row * (resolution - 2); + /* Create vertical columns. + * + * At first iteration it will be edges (0-3. 1-4, 2-5), then it + * will be (3-6, 4-7, 5-8) and so on. + */ + subdiv_edge = subdiv_create_edges_column( + ctx, + subdiv_edge, + NULL, + NULL, + start_row_vertex_index, + resolution - 2); + /* Create horizontal edge row. + * + * At first iteration it will be edges (3-4, 4-5), then it will be + * (6-7, 7-8) and so on. + */ + subdiv_edge = subdiv_create_edges_row( + ctx, + subdiv_edge, + NULL, + start_row_vertex_index + resolution - 2, + resolution - 2); + } + /* Connect inner part of patch to boundary. */ + for (int corner = 0; corner < coarse_poly->totloop; corner++) { + const MLoop *coarse_loop = + &coarse_mloop[coarse_poly->loopstart + corner]; + const MEdge *coarse_edge = &coarse_medge[coarse_loop->e]; + const int start_edge_vertex = ctx->vertices_edge_offset + + coarse_loop->e * num_subdiv_vertices_per_coarse_edge; + const bool flip = (coarse_edge->v2 == coarse_loop->v); + int side_start_index = start_vertex_index; + int side_stride = 0; + /* Calculate starting veretx of corresponding inner part of ptex. */ + if (corner == 0) { + side_stride = 1; + } + else if (corner == 1) { + side_start_index += resolution - 3; + side_stride = resolution - 2; + } + else if (corner == 2) { + side_start_index += num_subdiv_vertices_per_coarse_edge * + num_subdiv_vertices_per_coarse_edge - 1; + side_stride = -1; + } + else if (corner == 3) { + side_start_index += num_subdiv_vertices_per_coarse_edge * + (num_subdiv_vertices_per_coarse_edge - 1); + side_stride = -(resolution - 2); + } + for (int i = 0; i < resolution - 2; i++, subdiv_edge++) { + subdiv_copy_edge_data(ctx, subdiv_edge, NULL); + if (flip) { + subdiv_edge->v1 = start_edge_vertex + (resolution - i - 3); + } + else { + subdiv_edge->v1 = start_edge_vertex + i; + } + subdiv_edge->v2 = side_start_index + side_stride * i; + } + } +} + +static void subdiv_create_edges_all_patches_special( + SubdivMeshContext *ctx, + const MPoly *coarse_poly) +{ + const Mesh *coarse_mesh = ctx->coarse_mesh; + const MEdge *coarse_medge = coarse_mesh->medge; + const MLoop *coarse_mloop = coarse_mesh->mloop; + const MPoly *coarse_mpoly = coarse_mesh->mpoly; + const int poly_index = coarse_poly - coarse_mpoly; + const int resolution = ctx->settings->resolution; + const int ptex_face_resolution = + ptex_face_resolution_get(coarse_poly, resolution); + const int ptex_face_inner_resolution = ptex_face_resolution - 2; + const int num_inner_vertices_per_ptex = + (ptex_face_resolution - 1) * (ptex_face_resolution - 2); + const int num_subdiv_vertices_per_coarse_edge = resolution - 2; + const int center_vertex_index = + ctx->vertices_inner_offset + + ctx->subdiv_vertex_offset[poly_index]; + const int start_vertex_index = center_vertex_index + 1; + Mesh *subdiv_mesh = ctx->subdiv_mesh; + MEdge *subdiv_medge = subdiv_mesh->medge; + MEdge *subdiv_edge = &subdiv_medge[ + ctx->edge_inner_offset + ctx->subdiv_edge_offset[poly_index]]; + /* Create inner ptex edges. */ + for (int corner = 0; corner < coarse_poly->totloop; corner++) { + const int start_ptex_face_vertex_index = + start_vertex_index + corner * num_inner_vertices_per_ptex; + /* Similar steps to regular patch case. */ + subdiv_edge = subdiv_create_edges_row( + ctx, + subdiv_edge, + NULL, + start_ptex_face_vertex_index, + ptex_face_inner_resolution + 1); + for (int row = 0; row < ptex_face_inner_resolution - 1; row++) { + const int start_row_vertex_index = + start_ptex_face_vertex_index + + row * (ptex_face_inner_resolution + 1); + subdiv_edge = subdiv_create_edges_column( + ctx, + subdiv_edge, + NULL, + NULL, + start_row_vertex_index, + ptex_face_inner_resolution + 1); + subdiv_edge = subdiv_create_edges_row( + ctx, + subdiv_edge, + NULL, + start_row_vertex_index + ptex_face_inner_resolution + 1, + ptex_face_inner_resolution + 1); + } + } + /* Create connections between ptex faces. */ + for (int corner = 0; corner < coarse_poly->totloop; corner++) { + const int next_corner = (corner + 1) % coarse_poly->totloop; + int current_patch_vertex_index = + start_vertex_index + corner * num_inner_vertices_per_ptex + + ptex_face_inner_resolution; + int next_path_vertex_index = + start_vertex_index + next_corner * num_inner_vertices_per_ptex + + num_inner_vertices_per_ptex - ptex_face_resolution + 1; + for (int row = 0; + row < ptex_face_inner_resolution; + row++, subdiv_edge++) + { + subdiv_copy_edge_data(ctx, subdiv_edge, NULL); + subdiv_edge->v1 = current_patch_vertex_index; + subdiv_edge->v2 = next_path_vertex_index; + current_patch_vertex_index += ptex_face_inner_resolution + 1; + next_path_vertex_index += 1; + } + } + /* Create edges from center. */ + if (ptex_face_resolution >= 3) { + for (int corner = 0; + corner < coarse_poly->totloop; + corner++, subdiv_edge++) + { + const int current_patch_end_vertex_index = + start_vertex_index + corner * num_inner_vertices_per_ptex + + num_inner_vertices_per_ptex - 1; + subdiv_copy_edge_data(ctx, subdiv_edge, NULL); + subdiv_edge->v1 = center_vertex_index; + subdiv_edge->v2 = current_patch_end_vertex_index; + } + } + /* Connect inner path of patch to boundary. */ + const MLoop *prev_coarse_loop = + &coarse_mloop[coarse_poly->loopstart + coarse_poly->totloop - 1]; + for (int corner = 0; corner < coarse_poly->totloop; corner++) { + const MLoop *coarse_loop = + &coarse_mloop[coarse_poly->loopstart + corner]; + { + const MEdge *coarse_edge = &coarse_medge[coarse_loop->e]; + const int start_edge_vertex = ctx->vertices_edge_offset + + coarse_loop->e * num_subdiv_vertices_per_coarse_edge; + const bool flip = (coarse_edge->v2 == coarse_loop->v); + int side_start_index; + if (ptex_face_resolution >= 3) { + side_start_index = + start_vertex_index + num_inner_vertices_per_ptex * corner; + } + else { + side_start_index = center_vertex_index; + } + for (int i = 0; i < ptex_face_resolution - 1; i++, subdiv_edge++) { + subdiv_copy_edge_data(ctx, subdiv_edge, NULL); + if (flip) { + subdiv_edge->v1 = start_edge_vertex + (resolution - i - 3); + } + else { + subdiv_edge->v1 = start_edge_vertex + i; + } + subdiv_edge->v2 = side_start_index + i; + } + } + if (ptex_face_resolution >= 3) { + const MEdge *coarse_edge = &coarse_medge[prev_coarse_loop->e]; + const int start_edge_vertex = ctx->vertices_edge_offset + + prev_coarse_loop->e * num_subdiv_vertices_per_coarse_edge; + const bool flip = (coarse_edge->v2 == coarse_loop->v); + int side_start_index = + start_vertex_index + num_inner_vertices_per_ptex * corner; + for (int i = 0; i < ptex_face_resolution - 2; i++, subdiv_edge++) { + subdiv_copy_edge_data(ctx, subdiv_edge, NULL); + if (flip) { + subdiv_edge->v1 = start_edge_vertex + (resolution - i - 3); + } + else { + subdiv_edge->v1 = start_edge_vertex + i; + } + subdiv_edge->v2 = side_start_index + + (ptex_face_inner_resolution + 1) * i; + } + } + prev_coarse_loop = coarse_loop; + } +} + +static void subdiv_create_edges_all_patches( + SubdivMeshContext *ctx, + const MPoly *coarse_poly) +{ + if (coarse_poly->totloop == 4) { + subdiv_create_edges_all_patches_regular(ctx, coarse_poly); + } + else { + subdiv_create_edges_all_patches_special(ctx, coarse_poly); + } +} + +static void subdiv_create_edges(SubdivMeshContext *ctx, int poly_index) +{ + const Mesh *coarse_mesh = ctx->coarse_mesh; + const MPoly *coarse_mpoly = coarse_mesh->mpoly; + const MPoly *coarse_poly = &coarse_mpoly[poly_index]; + subdiv_create_edges_all_patches(ctx, coarse_poly); +} + +static void subdiv_create_boundary_edges( + SubdivMeshContext *ctx, + int edge_index) +{ + const Mesh *coarse_mesh = ctx->coarse_mesh; + const MEdge *coarse_medge = coarse_mesh->medge; + const MEdge *coarse_edge = &coarse_medge[edge_index]; + const int resolution = ctx->settings->resolution; + const int num_subdiv_vertices_per_coarse_edge = resolution - 2; + const int num_subdiv_edges_per_coarse_edge = resolution - 1; + Mesh *subdiv_mesh = ctx->subdiv_mesh; + MEdge *subdiv_medge = subdiv_mesh->medge; + MEdge *subdiv_edge = &subdiv_medge[ + ctx->edge_boundary_offset + + edge_index * num_subdiv_edges_per_coarse_edge]; + int last_vertex_index = ctx->vertices_corner_offset + coarse_edge->v1; + for (int i = 0; + i < num_subdiv_edges_per_coarse_edge - 1; + i++, subdiv_edge++) + { + subdiv_copy_edge_data(ctx, subdiv_edge, coarse_edge); + subdiv_edge->v1 = last_vertex_index; + subdiv_edge->v2 = + ctx->vertices_edge_offset + + edge_index * num_subdiv_vertices_per_coarse_edge + + i; + last_vertex_index = subdiv_edge->v2; + } + subdiv_copy_edge_data(ctx, subdiv_edge, coarse_edge); + subdiv_edge->v1 = last_vertex_index; + subdiv_edge->v2 = ctx->vertices_corner_offset + coarse_edge->v2; +} + +/* ============================================================================= + * Loops creation/interpolation. + */ + +static void subdiv_copy_loop_data( + const SubdivMeshContext *ctx, + MLoop *subdiv_loop, + const LoopsForInterpolation *loop_interpolation, + const float u, const float v) +{ + const int subdiv_loop_index = subdiv_loop - ctx->subdiv_mesh->mloop; + const float weights[4] = {(1.0f - u) * (1.0f - v), + u * (1.0f - v), + u * v, + (1.0f - u) * v}; + CustomData_interp(loop_interpolation->loop_data, + &ctx->subdiv_mesh->ldata, + loop_interpolation->loop_indices, + weights, NULL, + 4, + subdiv_loop_index); + /* TODO(sergey): Set ORIGINDEX. */ +} + +static void subdiv_eval_uv_layer(SubdivMeshContext *ctx, + MLoop *subdiv_loop, + const int ptex_face_index, + const float u, const float v, + const float du, const float dv) +{ + if (ctx->num_uv_layers == 0) { + return; + } + Subdiv *subdiv = ctx->subdiv; + const int mloop_index = subdiv_loop - ctx->subdiv_mesh->mloop; + for (int layer_index = 0; layer_index < ctx->num_uv_layers; layer_index++) { + MLoopUV *subdiv_loopuv = &ctx->uv_layers[layer_index][mloop_index]; + BKE_subdiv_eval_face_varying(subdiv, + layer_index, + ptex_face_index, + u, v, + subdiv_loopuv[0].uv); + BKE_subdiv_eval_face_varying(subdiv, + layer_index, + ptex_face_index, + u + du, v, + subdiv_loopuv[1].uv); + BKE_subdiv_eval_face_varying(subdiv, + layer_index, + ptex_face_index, + u + du, v + dv, + subdiv_loopuv[2].uv); + BKE_subdiv_eval_face_varying(subdiv, + layer_index, + ptex_face_index, + u, v + dv, + subdiv_loopuv[3].uv); + } +} + +static void rotate_indices(const int rot, int *a, int *b, int *c, int *d) +{ + int values[4] = {*a, *b, *c, *d}; + *a = values[(0 - rot + 4) % 4]; + *b = values[(1 - rot + 4) % 4]; + *c = values[(2 - rot + 4) % 4]; + *d = values[(3 - rot + 4) % 4]; +} + +static void subdiv_create_loops_of_poly( + SubdivMeshContext *ctx, + LoopsForInterpolation *loop_interpolation, + MLoop *subdiv_loop_start, + const int ptex_face_index, + const int rotation, + /*const*/ int v0, /*const*/ int e0, + /*const*/ int v1, /*const*/ int e1, + /*const*/ int v2, /*const*/ int e2, + /*const*/ int v3, /*const*/ int e3, + const float u, const float v, + const float du, const float dv) +{ + rotate_indices(rotation, &v0, &v1, &v2, &v3); + rotate_indices(rotation, &e0, &e1, &e2, &e3); + subdiv_copy_loop_data(ctx, + &subdiv_loop_start[0], + loop_interpolation, + u, v); + subdiv_loop_start[0].v = v0; + subdiv_loop_start[0].e = e0; + subdiv_copy_loop_data(ctx, + &subdiv_loop_start[1], + loop_interpolation, + u + du, v); + subdiv_loop_start[1].v = v1; + subdiv_loop_start[1].e = e1; + subdiv_copy_loop_data(ctx, + &subdiv_loop_start[2], + loop_interpolation, + u + du, v + dv); + subdiv_loop_start[2].v = v2; + subdiv_loop_start[2].e = e2; + subdiv_copy_loop_data(ctx, + &subdiv_loop_start[3], + loop_interpolation, + u, v + dv); + subdiv_loop_start[3].v = v3; + subdiv_loop_start[3].e = e3; + /* Interpolate UV layers using OpenSubdiv. */ + subdiv_eval_uv_layer(ctx, + subdiv_loop_start, + ptex_face_index, + u, v, du, dv); +} + +static void subdiv_create_loops_regular(SubdivMeshContext *ctx, + const MPoly *coarse_poly) +{ + const int resolution = ctx->settings->resolution; + /* Base/coarse mesh information. */ + const Mesh *coarse_mesh = ctx->coarse_mesh; + const MEdge *coarse_medge = coarse_mesh->medge; + const MLoop *coarse_mloop = coarse_mesh->mloop; + const MPoly *coarse_mpoly = coarse_mesh->mpoly; + const int poly_index = coarse_poly - coarse_mpoly; + const int ptex_resolution = + ptex_face_resolution_get(coarse_poly, resolution); + const int ptex_inner_resolution = ptex_resolution - 2; + const int num_subdiv_edges_per_coarse_edge = resolution - 1; + const int num_subdiv_vertices_per_coarse_edge = resolution - 2; + const float inv_ptex_resolution_1 = 1.0f / (float)(ptex_resolution - 1); + const int ptex_face_index = ctx->face_ptex_offset[poly_index]; + const int start_vertex_index = + ctx->vertices_inner_offset + + ctx->subdiv_vertex_offset[poly_index]; + const int start_edge_index = + ctx->edge_inner_offset + + ctx->subdiv_edge_offset[poly_index]; + const int start_poly_index = ctx->subdiv_polygon_offset[poly_index]; + const int start_loop_index = 4 * start_poly_index; + const float du = inv_ptex_resolution_1; + const float dv = inv_ptex_resolution_1; + /* Hi-poly subdivided mesh. */ + Mesh *subdiv_mesh = ctx->subdiv_mesh; + MLoop *subdiv_loopoop = subdiv_mesh->mloop; + MLoop *subdiv_loop = &subdiv_loopoop[start_loop_index]; + LoopsForInterpolation loop_interpolation; + loop_interpolation_init(ctx, &loop_interpolation, coarse_poly); + loop_interpolation_from_ptex(ctx, + &loop_interpolation, + coarse_poly, + 0); + /* Loops for inner part of ptex. */ + for (int y = 1; y < ptex_resolution - 2; y++) { + const float v = y * inv_ptex_resolution_1; + const int inner_y = y - 1; + for (int x = 1; x < ptex_resolution - 2; x++, subdiv_loop += 4) { + const int inner_x = x - 1; + const float u = x * inv_ptex_resolution_1; + /* Vertex indicies ordered counter-clockwise. */ + const int v0 = start_vertex_index + + (inner_y * ptex_inner_resolution + inner_x); + const int v1 = v0 + 1; + const int v2 = v0 + ptex_inner_resolution + 1; + const int v3 = v0 + ptex_inner_resolution; + /* Edge indicies ordered counter-clockwise. */ + const int e0 = start_edge_index + + (inner_y * (2 * ptex_inner_resolution - 1) + inner_x); + const int e1 = e0 + ptex_inner_resolution; + const int e2 = e0 + (2 * ptex_inner_resolution - 1); + const int e3 = e0 + ptex_inner_resolution - 1; + subdiv_create_loops_of_poly( + ctx, &loop_interpolation, subdiv_loop, ptex_face_index, 0, + v0, e0, v1, e1, v2, e2, v3, e3, + u, v, du, dv); + } + } + /* Loops for faces connecting inner ptex part with boundary. */ + const MLoop *prev_coarse_loop = + &coarse_mloop[coarse_poly->loopstart + coarse_poly->totloop - 1]; + for (int corner = 0; corner < coarse_poly->totloop; corner++) { + const MLoop *coarse_loop = + &coarse_mloop[coarse_poly->loopstart + corner]; + const MEdge *coarse_edge = &coarse_medge[coarse_loop->e]; + const MEdge *prev_coarse_edge = &coarse_medge[prev_coarse_loop->e]; + const int start_edge_vertex = ctx->vertices_edge_offset + + coarse_loop->e * num_subdiv_vertices_per_coarse_edge; + const bool flip = (coarse_edge->v2 == coarse_loop->v); + int side_start_index = start_vertex_index; + int side_stride = 0; + int v0 = ctx->vertices_corner_offset + coarse_loop->v; + int v3, e3; + int e2_offset, e2_stride; + float u, v, delta_u, delta_v; + if (prev_coarse_loop->v == prev_coarse_edge->v1) { + v3 = ctx->vertices_edge_offset + + prev_coarse_loop->e * num_subdiv_vertices_per_coarse_edge + + num_subdiv_vertices_per_coarse_edge - 1; + e3 = ctx->edge_boundary_offset + + prev_coarse_loop->e * num_subdiv_edges_per_coarse_edge + + num_subdiv_edges_per_coarse_edge - 1; + } + else { + v3 = ctx->vertices_edge_offset + + prev_coarse_loop->e * num_subdiv_vertices_per_coarse_edge; + e3 = ctx->edge_boundary_offset + + prev_coarse_loop->e * num_subdiv_edges_per_coarse_edge; + } + /* Calculate starting veretx of corresponding inner part of ptex. */ + if (corner == 0) { + side_stride = 1; + e2_offset = 0; + e2_stride = 1; + u = 0.0f; + v = 0.0f; + delta_u = du; + delta_v = 0.0f; + } + else if (corner == 1) { + side_start_index += resolution - 3; + side_stride = resolution - 2; + e2_offset = 2 * num_subdiv_edges_per_coarse_edge - 4; + e2_stride = 2 * num_subdiv_edges_per_coarse_edge - 3; + u = 1.0f - du; + v = 0; + delta_u = 0.0f; + delta_v = dv; + } + else if (corner == 2) { + side_start_index += num_subdiv_vertices_per_coarse_edge * + num_subdiv_vertices_per_coarse_edge - 1; + side_stride = -1; + e2_offset = num_edges_per_ptex_face_get(resolution - 2) - 1; + e2_stride = -1; + u = 1.0f - du; + v = 1.0f - dv; + delta_u = -du; + delta_v = 0.0f; + } + else if (corner == 3) { + side_start_index += num_subdiv_vertices_per_coarse_edge * + (num_subdiv_vertices_per_coarse_edge - 1); + side_stride = -(resolution - 2); + e2_offset = num_edges_per_ptex_face_get(resolution - 2) - + (2 * num_subdiv_edges_per_coarse_edge - 3); + e2_stride = -(2 * num_subdiv_edges_per_coarse_edge - 3); + u = 0.0f; + v = 1.0f - dv; + delta_u = 0.0f; + delta_v = -dv; + } + for (int i = 0; i < resolution - 2; i++, subdiv_loop += 4) { + int v1; + if (flip) { + v1 = start_edge_vertex + (resolution - i - 3); + } + else { + v1 = start_edge_vertex + i; + } + const int v2 = side_start_index + side_stride * i; + int e0; + if (flip) { + e0 = ctx->edge_boundary_offset + + coarse_loop->e * num_subdiv_edges_per_coarse_edge + + num_subdiv_edges_per_coarse_edge - i - 1; + } + else { + e0 = ctx->edge_boundary_offset + + coarse_loop->e * num_subdiv_edges_per_coarse_edge + + i; + } + int e1 = start_edge_index + + num_edges_per_ptex_face_get(resolution - 2) + + corner * num_subdiv_vertices_per_coarse_edge + + i; + int e2; + if (i == 0) { + e2 = start_edge_index + + num_edges_per_ptex_face_get(resolution - 2) + + ((corner - 1 + coarse_poly->totloop) % + coarse_poly->totloop) * + num_subdiv_vertices_per_coarse_edge + + num_subdiv_vertices_per_coarse_edge - 1; + } + else { + e2 = start_edge_index + e2_offset + e2_stride * (i - 1); + } + subdiv_create_loops_of_poly( + ctx, &loop_interpolation, subdiv_loop, + ptex_face_index, corner, + v0, e0, v1, e1, v2, e2, v3, e3, + u + delta_u * i, v + delta_v * i, du, dv); + v0 = v1; + v3 = v2; + e3 = e1; + } + prev_coarse_loop = coarse_loop; + } + loop_interpolation_end(&loop_interpolation); +} + +static void subdiv_create_loops_special(SubdivMeshContext *ctx, + const MPoly *coarse_poly) +{ + const int resolution = ctx->settings->resolution; + /* Base/coarse mesh information. */ + const Mesh *coarse_mesh = ctx->coarse_mesh; + const MEdge *coarse_medge = coarse_mesh->medge; + const MLoop *coarse_mloop = coarse_mesh->mloop; + const MPoly *coarse_mpoly = coarse_mesh->mpoly; + const int poly_index = coarse_poly - coarse_mpoly; + const int ptex_face_resolution = + ptex_face_resolution_get(coarse_poly, resolution); + const int ptex_face_inner_resolution = ptex_face_resolution - 2; + const float inv_ptex_resolution_1 = + 1.0f / (float)(ptex_face_resolution - 1); + const int num_inner_vertices_per_ptex = + (ptex_face_resolution - 1) * (ptex_face_resolution - 2); + const int num_inner_edges_per_ptex_face = + num_inner_edges_per_ptex_face_get( + ptex_face_inner_resolution + 1); + const int num_subdiv_vertices_per_coarse_edge = resolution - 2; + const int num_subdiv_edges_per_coarse_edge = resolution - 1; + const int ptex_face_index = ctx->face_ptex_offset[poly_index]; + const int center_vertex_index = + ctx->vertices_inner_offset + + ctx->subdiv_vertex_offset[poly_index]; + const int start_vertex_index = center_vertex_index + 1; + const int start_inner_vertex_index = center_vertex_index + 1; + const int start_edge_index = ctx->edge_inner_offset + + ctx->subdiv_edge_offset[poly_index]; + const int start_poly_index = ctx->subdiv_polygon_offset[poly_index]; + const int start_loop_index = 4 * start_poly_index; + const float du = inv_ptex_resolution_1; + const float dv = inv_ptex_resolution_1; + /* Hi-poly subdivided mesh. */ + Mesh *subdiv_mesh = ctx->subdiv_mesh; + MLoop *subdiv_loopoop = subdiv_mesh->mloop; + MLoop *subdiv_loop = &subdiv_loopoop[start_loop_index]; + LoopsForInterpolation loop_interpolation; + loop_interpolation_init(ctx, &loop_interpolation, coarse_poly); + for (int corner = 0; + corner < coarse_poly->totloop; + corner++) + { + const int corner_vertex_index = + start_vertex_index + corner * num_inner_vertices_per_ptex; + const int corner_edge_index = + start_edge_index + corner * num_inner_edges_per_ptex_face; + loop_interpolation_from_ptex(ctx, + &loop_interpolation, + coarse_poly, + corner); + for (int y = 1; y < ptex_face_inner_resolution; y++) { + const float v = y * inv_ptex_resolution_1; + const int inner_y = y - 1; + for (int x = 1; + x < ptex_face_inner_resolution + 1; + x++, subdiv_loop += 4) + { + const int inner_x = x - 1; + const float u = x * inv_ptex_resolution_1; + /* Vertex indicies ordered counter-clockwise. */ + const int v0 = + corner_vertex_index + + (inner_y * (ptex_face_inner_resolution + 1) + inner_x); + const int v1 = v0 + 1; + const int v2 = v0 + ptex_face_inner_resolution + 2; + const int v3 = v0 + ptex_face_inner_resolution + 1; + /* Edge indicies ordered counter-clockwise. */ + const int e0 = corner_edge_index + + (inner_y * (2 * ptex_face_inner_resolution + 1) + inner_x); + const int e1 = e0 + ptex_face_inner_resolution + 1; + const int e2 = e0 + (2 * ptex_face_inner_resolution + 1); + const int e3 = e0 + ptex_face_inner_resolution; + subdiv_create_loops_of_poly( + ctx, &loop_interpolation, subdiv_loop, + ptex_face_index + corner, 0, + v0, e0, v1, e1, v2, e2, v3, e3, + u, v, du, dv); + } + } + } + /* Create connections between ptex faces. */ + for (int corner = 0; corner < coarse_poly->totloop; corner++) { + const int next_corner = (corner + 1) % coarse_poly->totloop; + const int corner_edge_index = + start_edge_index + corner * num_inner_edges_per_ptex_face; + const int next_corner_edge_index = + start_edge_index + next_corner * num_inner_edges_per_ptex_face; + int current_patch_vertex_index = + start_inner_vertex_index + + corner * num_inner_vertices_per_ptex + + ptex_face_inner_resolution; + int next_path_vertex_index = + start_inner_vertex_index + + next_corner * num_inner_vertices_per_ptex + + num_inner_vertices_per_ptex - ptex_face_resolution + 1; + int v0 = current_patch_vertex_index; + int v1 = next_path_vertex_index; + current_patch_vertex_index += ptex_face_inner_resolution + 1; + next_path_vertex_index += 1; + int e0 = start_edge_index + + coarse_poly->totloop * num_inner_edges_per_ptex_face + + corner * (ptex_face_resolution - 2); + int e1 = next_corner_edge_index + num_inner_edges_per_ptex_face - + ptex_face_resolution + 2; + int e3 = corner_edge_index + 2 * ptex_face_resolution - 4; + loop_interpolation_from_ptex(ctx, + &loop_interpolation, + coarse_poly, + next_corner); + for (int row = 1; + row < ptex_face_inner_resolution; + row++, subdiv_loop += 4) + { + const int v2 = next_path_vertex_index; + const int v3 = current_patch_vertex_index; + const int e2 = e0 + 1; + const float u = row * du; + const float v = 1.0f - dv; + subdiv_create_loops_of_poly( + ctx, &loop_interpolation, subdiv_loop, + ptex_face_index + next_corner, 3, + v0, e0, v1, e1, v2, e2, v3, e3, + u, v, du, dv); + current_patch_vertex_index += ptex_face_inner_resolution + 1; + next_path_vertex_index += 1; + v0 = v3; + v1 = v2; + e0 = e2; + e1 += 1; + e3 += 2 * ptex_face_resolution - 3; + } + } + /* Create loops from center. */ + if (ptex_face_resolution >= 3) { + const int start_center_edge_index = + start_edge_index + + (num_inner_edges_per_ptex_face + + ptex_face_inner_resolution) * coarse_poly->totloop; + const int start_boundary_edge = + start_edge_index + + coarse_poly->totloop * num_inner_edges_per_ptex_face + + ptex_face_inner_resolution - 1; + for (int corner = 0, prev_corner = coarse_poly->totloop - 1; + corner < coarse_poly->totloop; + prev_corner = corner, corner++, subdiv_loop += 4) + { + loop_interpolation_from_ptex(ctx, + &loop_interpolation, + coarse_poly, + corner); + const int corner_edge_index = + start_edge_index + + corner * num_inner_edges_per_ptex_face; + const int current_patch_end_vertex_index = + start_vertex_index + corner * num_inner_vertices_per_ptex + + num_inner_vertices_per_ptex - 1; + const int prev_current_patch_end_vertex_index = + start_vertex_index + prev_corner * + num_inner_vertices_per_ptex + + num_inner_vertices_per_ptex - 1; + const int v0 = center_vertex_index; + const int v1 = prev_current_patch_end_vertex_index; + const int v2 = current_patch_end_vertex_index - 1; + const int v3 = current_patch_end_vertex_index; + const int e0 = start_center_edge_index + prev_corner; + const int e1 = start_boundary_edge + + prev_corner * (ptex_face_inner_resolution); + const int e2 = corner_edge_index + + num_inner_edges_per_ptex_face - 1; + const int e3 = start_center_edge_index + corner; + const float u = 1.0f - du; + const float v = 1.0f - dv; + subdiv_create_loops_of_poly( + ctx, &loop_interpolation, subdiv_loop, + ptex_face_index + corner, 2, + v0, e0, v1, e1, v2, e2, v3, e3, + u, v, du, dv); + } + } + /* Loops for faces connecting inner ptex part with boundary. */ + const MLoop *prev_coarse_loop = + &coarse_mloop[coarse_poly->loopstart + coarse_poly->totloop - 1]; + for (int prev_corner = coarse_poly->totloop - 1, corner = 0; + corner < coarse_poly->totloop; + prev_corner = corner, corner++) + { + loop_interpolation_from_ptex(ctx, + &loop_interpolation, + coarse_poly, + corner); + const MLoop *coarse_loop = + &coarse_mloop[coarse_poly->loopstart + corner]; + const MEdge *coarse_edge = &coarse_medge[coarse_loop->e]; + const MEdge *prev_coarse_edge = &coarse_medge[prev_coarse_loop->e]; + const bool flip = (coarse_edge->v2 == coarse_loop->v); + const int start_edge_vertex = ctx->vertices_edge_offset + + coarse_loop->e * num_subdiv_vertices_per_coarse_edge; + const int corner_vertex_index = + start_vertex_index + corner * num_inner_vertices_per_ptex; + const int corner_edge_index = + start_edge_index + corner * num_inner_edges_per_ptex_face; + /* Create loops for polygons along U axis. */ + int v0 = ctx->vertices_corner_offset + coarse_loop->v; + int v3, e3; + if (prev_coarse_loop->v == prev_coarse_edge->v1) { + v3 = ctx->vertices_edge_offset + + prev_coarse_loop->e * num_subdiv_vertices_per_coarse_edge + + num_subdiv_vertices_per_coarse_edge - 1; + e3 = ctx->edge_boundary_offset + + prev_coarse_loop->e * num_subdiv_edges_per_coarse_edge + + num_subdiv_edges_per_coarse_edge - 1; + } + else { + v3 = ctx->vertices_edge_offset + + prev_coarse_loop->e * num_subdiv_vertices_per_coarse_edge; + e3 = ctx->edge_boundary_offset + + prev_coarse_loop->e * num_subdiv_edges_per_coarse_edge; + } + for (int i = 0; + i <= ptex_face_inner_resolution; + i++, subdiv_loop += 4) + { + int v1; + if (flip) { + v1 = start_edge_vertex + (resolution - i - 3); + } + else { + v1 = start_edge_vertex + i; + } + int v2; + if (ptex_face_inner_resolution >= 1) { + v2 = corner_vertex_index + i; + } + else { + v2 = center_vertex_index; + } + int e0; + if (flip) { + e0 = ctx->edge_boundary_offset + + coarse_loop->e * num_subdiv_edges_per_coarse_edge + + num_subdiv_edges_per_coarse_edge - i - 1; + } + else { + e0 = ctx->edge_boundary_offset + + coarse_loop->e * num_subdiv_edges_per_coarse_edge + + i; + } + int e1 = start_edge_index + + corner * (2 * ptex_face_inner_resolution + 1); + if (ptex_face_resolution >= 3) { + e1 += coarse_poly->totloop * (num_inner_edges_per_ptex_face + + ptex_face_inner_resolution + 1) + + i; + } + int e2 = 0; + if (i == 0 && ptex_face_resolution >= 3) { + e2 = start_edge_index + + coarse_poly->totloop * + (num_inner_edges_per_ptex_face + + ptex_face_inner_resolution + 1) + + corner * (2 * ptex_face_inner_resolution + 1) + + ptex_face_inner_resolution + 1; + } + else if (i == 0 && ptex_face_resolution < 3) { + e2 = start_edge_index + + prev_corner * (2 * ptex_face_inner_resolution + 1); + } + else { + e2 = corner_edge_index + i - 1; + } + const float u = du * i; + const float v = 0.0f; + subdiv_create_loops_of_poly( + ctx, &loop_interpolation, subdiv_loop, + ptex_face_index + corner, 0, + v0, e0, v1, e1, v2, e2, v3, e3, + u, v, du, dv); + v0 = v1; + v3 = v2; + e3 = e1; + } + /* Create loops for polygons along V axis. */ + const bool flip_prev = (prev_coarse_edge->v2 == coarse_loop->v); + v0 = corner_vertex_index; + if (prev_coarse_loop->v == prev_coarse_edge->v1) { + v3 = ctx->vertices_edge_offset + + prev_coarse_loop->e * num_subdiv_vertices_per_coarse_edge + + num_subdiv_vertices_per_coarse_edge - 1; + } + else { + v3 = ctx->vertices_edge_offset + + prev_coarse_loop->e * num_subdiv_vertices_per_coarse_edge; + } + e3 = start_edge_index + + coarse_poly->totloop * + (num_inner_edges_per_ptex_face + + ptex_face_inner_resolution + 1) + + corner * (2 * ptex_face_inner_resolution + 1) + + ptex_face_inner_resolution + 1; + for (int i = 0; + i <= ptex_face_inner_resolution - 1; + i++, subdiv_loop += 4) + { + int v1; + int e0, e1; + if (i == ptex_face_inner_resolution - 1) { + v1 = start_vertex_index + + prev_corner * num_inner_vertices_per_ptex + + ptex_face_inner_resolution; + e1 = start_edge_index + + coarse_poly->totloop * + (num_inner_edges_per_ptex_face + + ptex_face_inner_resolution + 1) + + prev_corner * (2 * ptex_face_inner_resolution + 1) + + ptex_face_inner_resolution; + e0 = start_edge_index + + coarse_poly->totloop * num_inner_edges_per_ptex_face + + prev_corner * ptex_face_inner_resolution; + } + else { + v1 = v0 + ptex_face_inner_resolution + 1; + e0 = corner_edge_index + ptex_face_inner_resolution + + i * (2 * ptex_face_inner_resolution + 1); + e1 = e3 + 1; + } + int v2 = flip_prev ? v3 - 1 : v3 + 1; + int e2; + if (flip_prev) { + e2 = ctx->edge_boundary_offset + + prev_coarse_loop->e * + num_subdiv_edges_per_coarse_edge + + num_subdiv_edges_per_coarse_edge - 2 - i; + } + else { + e2 = ctx->edge_boundary_offset + + prev_coarse_loop->e * + num_subdiv_edges_per_coarse_edge + 1 + i; + } + const float u = 0.0f; + const float v = du * (i + 1); + subdiv_create_loops_of_poly( + ctx, &loop_interpolation, subdiv_loop, + ptex_face_index + corner, 1, + v0, e0, v1, e1, v2, e2, v3, e3, + u, v, du, dv); + v0 = v1; + v3 = v2; + e3 = e1; + } + prev_coarse_loop = coarse_loop; + } + loop_interpolation_end(&loop_interpolation); +} + +static void subdiv_create_loops(SubdivMeshContext *ctx, int poly_index) +{ + const Mesh *coarse_mesh = ctx->coarse_mesh; + const MPoly *coarse_mpoly = coarse_mesh->mpoly; + const MPoly *coarse_poly = &coarse_mpoly[poly_index]; + if (coarse_poly->totloop == 4) { + subdiv_create_loops_regular(ctx, coarse_poly); + } + else { + subdiv_create_loops_special(ctx, coarse_poly); + } +} + +/* ============================================================================= + * Polygons subdivision process. + */ + +static void subdiv_copy_poly_data(const SubdivMeshContext *ctx, + MPoly *subdiv_poly, + const MPoly *coarse_poly) +{ + const int coarse_poly_index = coarse_poly - ctx->coarse_mesh->mpoly; + const int subdiv_poly_index = subdiv_poly - ctx->subdiv_mesh->mpoly; + CustomData_copy_data(&ctx->coarse_mesh->pdata, + &ctx->subdiv_mesh->pdata, + coarse_poly_index, + subdiv_poly_index, + 1); +} + +static void subdiv_create_polys(SubdivMeshContext *ctx, int poly_index) +{ + const int resolution = ctx->settings->resolution; + const int start_poly_index = ctx->subdiv_polygon_offset[poly_index]; + /* Base/coarse mesh information. */ + const Mesh *coarse_mesh = ctx->coarse_mesh; + const MPoly *coarse_mpoly = coarse_mesh->mpoly; + const MPoly *coarse_poly = &coarse_mpoly[poly_index]; + const int num_ptex_faces_per_poly = + num_ptex_faces_per_poly_get(coarse_poly); + const int ptex_resolution = + ptex_face_resolution_get(coarse_poly, resolution); + const int num_polys_per_ptex = num_polys_per_ptex_get(ptex_resolution); + const int num_loops_per_ptex = 4 * num_polys_per_ptex; + const int start_loop_index = 4 * start_poly_index; + /* Hi-poly subdivided mesh. */ + Mesh *subdiv_mesh = ctx->subdiv_mesh; + MPoly *subdiv_mpoly = subdiv_mesh->mpoly; + MPoly *subdiv_mp = &subdiv_mpoly[start_poly_index]; + for (int ptex_of_poly_index = 0; + ptex_of_poly_index < num_ptex_faces_per_poly; + ptex_of_poly_index++) + { + for (int subdiv_poly_index = 0; + subdiv_poly_index < num_polys_per_ptex; + subdiv_poly_index++, subdiv_mp++) + { + subdiv_copy_poly_data(ctx, subdiv_mp, coarse_poly); + subdiv_mp->loopstart = start_loop_index + + (ptex_of_poly_index * num_loops_per_ptex) + + (subdiv_poly_index * 4); + subdiv_mp->totloop = 4; + } + } +} + +/* ============================================================================= + * Loose elements subdivision process. + */ + +static void subdiv_create_loose_vertices_task( + void *__restrict userdata, + const int vertex_index, + const ParallelRangeTLS *__restrict UNUSED(tls)) +{ + SubdivMeshContext *ctx = userdata; + if (BLI_BITMAP_TEST_BOOL(ctx->coarse_vertices_used_map, vertex_index)) { + /* Vertex is not loose, was handled when handling polygons. */ + return; + } + const Mesh *coarse_mesh = ctx->coarse_mesh; + const MVert *coarse_mvert = coarse_mesh->mvert; + const MVert *coarse_vertex = &coarse_mvert[vertex_index]; + Mesh *subdiv_mesh = ctx->subdiv_mesh; + MVert *subdiv_mvert = subdiv_mesh->mvert; + MVert *subdiv_vertex = &subdiv_mvert[ + ctx->vertices_corner_offset + vertex_index]; + subdiv_vertex_data_copy(ctx, coarse_vertex, subdiv_vertex); +} + +/* Get neighbor edges of the given one. + * - neighbors[0] is an edge adjacent to edge->v1. + * - neighbors[1] is an edge adjacent to edge->v1. + */ +static void find_edge_neighbors(const SubdivMeshContext *ctx, + const MEdge *edge, + const MEdge *neighbors[2]) +{ + const Mesh *coarse_mesh = ctx->coarse_mesh; + const MEdge *coarse_medge = coarse_mesh->medge; + neighbors[0] = NULL; + neighbors[1] = NULL; + for (int edge_index = 0; edge_index < coarse_mesh->totedge; edge_index++) { + if (BLI_BITMAP_TEST_BOOL(ctx->coarse_edges_used_map, edge_index)) { + continue; + } + const MEdge *current_edge = &coarse_medge[edge_index]; + if (current_edge == edge) { + continue; + } + if (ELEM(edge->v1, current_edge->v1, current_edge->v2)) { + neighbors[0] = current_edge; + } + if (ELEM(edge->v2, current_edge->v1, current_edge->v2)) { + neighbors[1] = current_edge; + } + } +} + +static void points_for_loose_edges_interpolation_get( + SubdivMeshContext *ctx, + const MEdge *coarse_edge, + const MEdge *neighbors[2], + float points_r[4][3]) +{ + const Mesh *coarse_mesh = ctx->coarse_mesh; + const MVert *coarse_mvert = coarse_mesh->mvert; + /* Middle points corresponds to the edge. */ + copy_v3_v3(points_r[1], coarse_mvert[coarse_edge->v1].co); + copy_v3_v3(points_r[2], coarse_mvert[coarse_edge->v2].co); + /* Start point, duplicate from edge start if no neighbor. */ + if (neighbors[0] != NULL) { + if (neighbors[0]->v1 == coarse_edge->v1) { + copy_v3_v3(points_r[0], coarse_mvert[neighbors[0]->v2].co); + } + else { + copy_v3_v3(points_r[0], coarse_mvert[neighbors[0]->v1].co); + } + } + else { + sub_v3_v3v3(points_r[0], points_r[1], points_r[2]); + add_v3_v3(points_r[0], points_r[1]); + } + /* End point, duplicate from edge end if no neighbor. */ + if (neighbors[1] != NULL) { + if (neighbors[1]->v1 == coarse_edge->v2) { + copy_v3_v3(points_r[3], coarse_mvert[neighbors[1]->v2].co); + } + else { + copy_v3_v3(points_r[3], coarse_mvert[neighbors[1]->v1].co); + } + } + else { + sub_v3_v3v3(points_r[3], points_r[2], points_r[1]); + add_v3_v3(points_r[3], points_r[2]); + } +} + +static void subdiv_create_vertices_of_loose_edges_task( + void *__restrict userdata, + const int edge_index, + const ParallelRangeTLS *__restrict UNUSED(tls)) +{ + SubdivMeshContext *ctx = userdata; + if (BLI_BITMAP_TEST_BOOL(ctx->coarse_edges_used_map, edge_index)) { + /* Vertex is not loose, was handled when handling polygons. */ + return; + } + const int resolution = ctx->settings->resolution; + const int resolution_1 = resolution - 1; + const float inv_resolution_1 = 1.0f / (float)resolution_1; + const int num_subdiv_vertices_per_coarse_edge = resolution - 2; + const Mesh *coarse_mesh = ctx->coarse_mesh; + const MEdge *coarse_edge = &coarse_mesh->medge[edge_index]; + Mesh *subdiv_mesh = ctx->subdiv_mesh; + MVert *subdiv_mvert = subdiv_mesh->mvert; + /* Find neighbors of the current loose edge. */ + const MEdge *neighbors[2]; + find_edge_neighbors(ctx, coarse_edge, neighbors); + /* Get points for b-spline interpolation. */ + float points[4][3]; + points_for_loose_edges_interpolation_get( + ctx, coarse_edge, neighbors, points); + /* Subdivion verticies which corresponds to edge's v1 and v2. */ + MVert *subdiv_v1 = &subdiv_mvert[ + ctx->vertices_corner_offset + coarse_edge->v1]; + MVert *subdiv_v2 = &subdiv_mvert[ + ctx->vertices_corner_offset + coarse_edge->v2]; + /* First subdivided inner vertex of the edge. */ + MVert *subdiv_start_vertex = &subdiv_mvert[ + ctx->vertices_edge_offset + + edge_index * num_subdiv_vertices_per_coarse_edge]; + /* Perform interpolation. */ + for (int i = 0; i < resolution; i++) { + const float u = i * inv_resolution_1; + float weights[4]; + key_curve_position_weights(u, weights, KEY_BSPLINE); + + MVert *subdiv_vertex; + if (i == 0) { + subdiv_vertex = subdiv_v1; + } + else if (i == resolution - 1) { + subdiv_vertex = subdiv_v2; + } + else { + subdiv_vertex = &subdiv_start_vertex[i - 1]; + } + interp_v3_v3v3v3v3(subdiv_vertex->co, + points[0], + points[1], + points[2], + points[3], + weights); + /* Reset flags and such. */ + subdiv_vertex->flag = 0; + subdiv_vertex->bweight = 0.0f; + /* Reset normal. */ + subdiv_vertex->no[0] = 0.0f; + subdiv_vertex->no[1] = 0.0f; + subdiv_vertex->no[2] = 1.0f; + } +} + +/* ============================================================================= + * Subdivision process entry points. + */ + +static void subdiv_eval_task( + void *__restrict userdata, + const int poly_index, + const ParallelRangeTLS *__restrict UNUSED(tls)) +{ + SubdivMeshContext *ctx = userdata; + /* Evaluate hi-poly vertex coordinates and normals. */ + subdiv_evaluate_vertices(ctx, poly_index); + /* Create mesh geometry for the given base poly index. */ + subdiv_create_edges(ctx, poly_index); + subdiv_create_loops(ctx, poly_index); + subdiv_create_polys(ctx, poly_index); +} + +static void subdiv_create_boundary_edges_task( + void *__restrict userdata, + const int edge_index, + const ParallelRangeTLS *__restrict UNUSED(tls)) +{ + SubdivMeshContext *ctx = userdata; + subdiv_create_boundary_edges(ctx, edge_index); +} + +Mesh *BKE_subdiv_to_mesh( + Subdiv *subdiv, + const SubdivToMeshSettings *settings, + const Mesh *coarse_mesh) +{ + BKE_subdiv_stats_begin(&subdiv->stats, SUBDIV_STATS_SUBDIV_TO_MESH); + /* Make sure evaluator is up to date with possible new topology, and that + * is is refined for the new positions of coarse vertices. + */ + BKE_subdiv_eval_update_from_mesh(subdiv, coarse_mesh); + SubdivMeshContext ctx = {0}; + ctx.coarse_mesh = coarse_mesh; + ctx.subdiv = subdiv; + ctx.settings = settings; + subdiv_mesh_ctx_init(&ctx); + Mesh *result = BKE_mesh_new_nomain_from_template( + coarse_mesh, + ctx.num_subdiv_vertices, + ctx.num_subdiv_edges, + 0, + ctx.num_subdiv_loops, + ctx.num_subdiv_polygons); + ctx.subdiv_mesh = result; + subdiv_mesh_ctx_init_result(&ctx); + /* Multi-threaded evaluation. */ + ParallelRangeSettings parallel_range_settings; + BKE_subdiv_stats_begin(&subdiv->stats, + SUBDIV_STATS_SUBDIV_TO_MESH_GEOMETRY); + BLI_parallel_range_settings_defaults(¶llel_range_settings); + BLI_task_parallel_range(0, coarse_mesh->totpoly, + &ctx, + subdiv_eval_task, + ¶llel_range_settings); + BLI_task_parallel_range(0, coarse_mesh->totvert, + &ctx, + subdiv_create_loose_vertices_task, + ¶llel_range_settings); + BLI_task_parallel_range(0, coarse_mesh->totedge, + &ctx, + subdiv_create_vertices_of_loose_edges_task, + ¶llel_range_settings); + BLI_task_parallel_range(0, coarse_mesh->totedge, + &ctx, + subdiv_create_boundary_edges_task, + ¶llel_range_settings); + subdiv_mesh_ctx_free(&ctx); + BKE_subdiv_stats_end(&subdiv->stats, SUBDIV_STATS_SUBDIV_TO_MESH_GEOMETRY); + // BKE_mesh_validate(result, true, true); + BKE_subdiv_stats_end(&subdiv->stats, SUBDIV_STATS_SUBDIV_TO_MESH); + return result; +} diff --git a/source/blender/blenkernel/intern/subdiv_stats.c b/source/blender/blenkernel/intern/subdiv_stats.c new file mode 100644 index 00000000000..f2219961ab7 --- /dev/null +++ b/source/blender/blenkernel/intern/subdiv_stats.c @@ -0,0 +1,84 @@ +/* + * ***** 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. + * + * The Original Code is Copyright (C) 2018 by Blender Foundation. + * All rights reserved. + * + * Contributor(s): Sergey Sharybin. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/blenkernel/intern/subdiv_stats.c + * \ingroup bke + */ + +#include "BKE_subdiv.h" + +#include <stdio.h> + +#include "PIL_time.h" + +void BKE_subdiv_stats_init(SubdivStats *stats) +{ + stats->topology_refiner_creation_time = 0.0; + stats->subdiv_to_mesh_time = 0.0; + stats->subdiv_to_mesh_geometry_time = 0.0; + stats->evaluator_creation_time = 0.0; + stats->evaluator_refine_time = 0.0; +} + +void BKE_subdiv_stats_begin(SubdivStats *stats, eSubdivStatsValue value) +{ + stats->begin_timestamp_[value] = PIL_check_seconds_timer(); +} + +void BKE_subdiv_stats_end(SubdivStats *stats, eSubdivStatsValue value) +{ + stats->values_[value] = + PIL_check_seconds_timer() - stats->begin_timestamp_[value]; +} + +void BKE_subdiv_stats_print(const SubdivStats *stats) +{ +#define STATS_PRINT_TIME(stats, value, description) \ + do { \ + if ((stats)->value > 0.0) { \ + printf(" %s: %f (sec)\n", description, (stats)->value); \ + } \ + } while (false) + + printf("Subdivision surface statistics:\n"); + + STATS_PRINT_TIME(stats, + topology_refiner_creation_time, + "Topology refiner creation time"); + STATS_PRINT_TIME(stats, + subdiv_to_mesh_time, + "Subdivision to mesh time"); + STATS_PRINT_TIME(stats, + subdiv_to_mesh_geometry_time, + " Geometry time"); + STATS_PRINT_TIME(stats, + evaluator_creation_time, + "Evaluator creation time"); + STATS_PRINT_TIME(stats, + evaluator_refine_time, + "Evaluator refine time"); + +#undef STATS_PRINT_TIME +} diff --git a/source/blender/blenkernel/intern/subsurf_ccg.c b/source/blender/blenkernel/intern/subsurf_ccg.c index 9ea6ef62e4e..cead75ae659 100644 --- a/source/blender/blenkernel/intern/subsurf_ccg.c +++ b/source/blender/blenkernel/intern/subsurf_ccg.c @@ -280,11 +280,11 @@ static void get_face_uv_map_vert(UvVertMap *vmap, struct MPoly *mpoly, struct ML for (nv = v = BKE_mesh_uv_vert_map_get_vert(vmap, ml[j].v); v; v = v->next) { if (v->separate) nv = v; - if (v->f == fi) + if (v->poly_index == fi) break; } - fverts[j] = SET_UINT_IN_POINTER(mpoly[nv->f].loopstart + nv->tfindex); + fverts[j] = SET_UINT_IN_POINTER(mpoly[nv->poly_index].loopstart + nv->loop_of_poly_index); } } @@ -292,7 +292,6 @@ static int ss_sync_from_uv(CCGSubSurf *ss, CCGSubSurf *origss, DerivedMesh *dm, { MPoly *mpoly = dm->getPolyArray(dm); MLoop *mloop = dm->getLoopArray(dm); - MVert *mvert = dm->getVertArray(dm); int totvert = dm->getNumVerts(dm); int totface = dm->getNumPolys(dm); int i, seam; @@ -304,13 +303,12 @@ static int ss_sync_from_uv(CCGSubSurf *ss, CCGSubSurf *origss, DerivedMesh *dm, BLI_array_declare(fverts); #endif EdgeSet *eset; - float creaseFactor = (float)ccgSubSurf_getSubdivisionLevels(ss); float uv[3] = {0.0f, 0.0f, 0.0f}; /* only first 2 values are written into */ limit[0] = limit[1] = STD_UV_CONNECT_LIMIT; /* previous behavior here is without accounting for winding, however this causes stretching in * UV map in really simple cases with mirror + subsurf, see second part of T44530. Also, initially - * intention is to treat merged vertices from mirror modifier as seams, see code below with ME_VERT_MERGED + * intention is to treat merged vertices from mirror modifier as seams. * This fixes a very old regression (2.49 was correct here) */ vmap = BKE_mesh_uv_vert_map_create(mpoly, mloop, mloopuv, totface, totvert, limit, false, true); if (!vmap) @@ -327,12 +325,12 @@ static int ss_sync_from_uv(CCGSubSurf *ss, CCGSubSurf *origss, DerivedMesh *dm, if (v->separate) break; - seam = (v != NULL) || ((mvert + i)->flag & ME_VERT_MERGED); + seam = (v != NULL); for (v = BKE_mesh_uv_vert_map_get_vert(vmap, i); v; v = v->next) { if (v->separate) { CCGVert *ssv; - int loopid = mpoly[v->f].loopstart + v->tfindex; + int loopid = mpoly[v->poly_index].loopstart + v->loop_of_poly_index; CCGVertHDL vhdl = SET_INT_IN_POINTER(loopid); copy_v2_v2(uv, mloopuv[loopid].uv); @@ -365,18 +363,11 @@ static int ss_sync_from_uv(CCGSubSurf *ss, CCGSubSurf *origss, DerivedMesh *dm, for (j = 0, j_next = nverts - 1; j < nverts; j_next = j++) { unsigned int v0 = GET_UINT_FROM_POINTER(fverts[j_next]); unsigned int v1 = GET_UINT_FROM_POINTER(fverts[j]); - MVert *mv0 = mvert + (ml[j_next].v); - MVert *mv1 = mvert + (ml[j].v); if (BLI_edgeset_add(eset, v0, v1)) { CCGEdge *e, *orige = ccgSubSurf_getFaceEdge(origf, j_next); CCGEdgeHDL ehdl = SET_INT_IN_POINTER(mp->loopstart + j_next); - float crease; - - if ((mv0->flag & mv1->flag) & ME_VERT_MERGED) - crease = creaseFactor; - else - crease = ccgSubSurf_getEdgeCrease(orige); + float crease = ccgSubSurf_getEdgeCrease(orige); ccgSubSurf_syncEdge(ss, ehdl, fverts[j_next], fverts[j], crease, &e); } diff --git a/source/blender/blenkernel/intern/undo_system.c b/source/blender/blenkernel/intern/undo_system.c index 173b459f442..0eb209cabeb 100644 --- a/source/blender/blenkernel/intern/undo_system.c +++ b/source/blender/blenkernel/intern/undo_system.c @@ -255,6 +255,7 @@ void BKE_undosys_stack_clear_active(UndoStack *ustack) static bool undosys_stack_push_main(UndoStack *ustack, const char *name, struct Main *bmain) { UNDO_NESTED_ASSERT(false); + BLI_assert(ustack->step_init == NULL); CLOG_INFO(&LOG, 1, "'%s'", name); bContext *C_temp = CTX_create(); CTX_data_main_set(C_temp, bmain); @@ -442,7 +443,13 @@ bool BKE_undosys_step_push_with_type(UndoStack *ustack, bContext *C, const char Main *bmain = G.main; if (bmain->is_memfile_undo_written == false) { const char *name_internal = "MemFile Internal"; - if (undosys_stack_push_main(ustack, name_internal, bmain)) { + /* Don't let 'step_init' cause issues when adding memfile undo step. */ + void *step_init = ustack->step_init; + ustack->step_init = NULL; + const bool ok = undosys_stack_push_main(ustack, name_internal, bmain); + /* Restore 'step_init'. */ + ustack->step_init = step_init; + if (ok) { UndoStep *us = ustack->steps.last; BLI_assert(STREQ(us->name, name_internal)); us->skip = true; @@ -865,7 +872,7 @@ ID *BKE_undosys_ID_map_lookup_with_prev(const UndoIDPtrMap *map, ID *id_src, ID return id_prev_match[1]; } else { - ID *id_dst = BKE_undosys_ID_map_lookup(map, id_src); + ID *id_dst = (id_src->lib == NULL) ? BKE_undosys_ID_map_lookup(map, id_src) : id_src; id_prev_match[0] = id_src; id_prev_match[1] = id_dst; return id_dst; diff --git a/source/blender/blenkernel/intern/world.c b/source/blender/blenkernel/intern/world.c index 3e1a9a4f57b..a5a38a6dc12 100644 --- a/source/blender/blenkernel/intern/world.c +++ b/source/blender/blenkernel/intern/world.c @@ -168,4 +168,3 @@ void BKE_world_make_local(Main *bmain, World *wrld, const bool lib_local) { BKE_id_make_local_generic(bmain, &wrld->id, true, lib_local); } - |