static void data_transfer_dtdata_type_preprocess(
        Object *UNUSED(ob_src), Object *UNUSED(ob_dst), DerivedMesh *dm_src, DerivedMesh *dm_dst, Mesh *me_dst,
        const int dtdata_type, const bool dirty_nors_dst, const bool use_split_nors_src, const float split_angle_src)
{
	if (dtdata_type == DT_TYPE_LNOR) {
		/* Compute custom normals into regular loop normals, which will be used for the transfer. */
		MVert *verts_dst = dm_dst ? dm_dst->getVertArray(dm_dst) : me_dst->mvert;
		const int num_verts_dst = dm_dst ? dm_dst->getNumVerts(dm_dst) : me_dst->totvert;
		MEdge *edges_dst = dm_dst ? dm_dst->getEdgeArray(dm_dst) : me_dst->medge;
		const int num_edges_dst = dm_dst ? dm_dst->getNumEdges(dm_dst) : me_dst->totedge;
		MPoly *polys_dst = dm_dst ? dm_dst->getPolyArray(dm_dst) : me_dst->mpoly;
		const int num_polys_dst = dm_dst ? dm_dst->getNumPolys(dm_dst) : me_dst->totpoly;
		MLoop *loops_dst = dm_dst ? dm_dst->getLoopArray(dm_dst) : me_dst->mloop;
		const int num_loops_dst = dm_dst ? dm_dst->getNumLoops(dm_dst) : me_dst->totloop;
		CustomData *pdata_dst = dm_dst ? dm_dst->getPolyDataLayout(dm_dst) : &me_dst->pdata;
		CustomData *ldata_dst = dm_dst ? dm_dst->getLoopDataLayout(dm_dst) : &me_dst->ldata;

		const bool use_split_nors_dst = (me_dst->flag & ME_AUTOSMOOTH) != 0;
		const float split_angle_dst = me_dst->smoothresh;

		dm_src->calcLoopNormals(dm_src, use_split_nors_src, split_angle_src);

		if (dm_dst) {
			dm_dst->calcLoopNormals(dm_dst, use_split_nors_dst, split_angle_dst);
		}
		else {
			float (*poly_nors_dst)[3];
			float (*loop_nors_dst)[3];
			short (*custom_nors_dst)[2] = CustomData_get_layer(ldata_dst, CD_CUSTOMLOOPNORMAL);

			/* Cache poly nors into a temp CDLayer. */
			poly_nors_dst = CustomData_get_layer(pdata_dst, CD_NORMAL);
			if (dirty_nors_dst || !poly_nors_dst) {
				if (!poly_nors_dst) {
					poly_nors_dst = CustomData_add_layer(pdata_dst, CD_NORMAL, CD_CALLOC, NULL, num_polys_dst);
					CustomData_set_layer_flag(pdata_dst, CD_NORMAL, CD_FLAG_TEMPORARY);
				}
				BKE_mesh_calc_normals_poly(verts_dst, num_verts_dst, loops_dst, polys_dst,
				                           num_loops_dst, num_polys_dst, poly_nors_dst, true);
			}
			/* Cache loop nors into a temp CDLayer. */
			loop_nors_dst = CustomData_get_layer(ldata_dst, CD_NORMAL);
			if (dirty_nors_dst || loop_nors_dst) {
				if (!loop_nors_dst) {
					loop_nors_dst = CustomData_add_layer(ldata_dst, CD_NORMAL, CD_CALLOC, NULL, num_loops_dst);
					CustomData_set_layer_flag(ldata_dst, CD_NORMAL, CD_FLAG_TEMPORARY);
				}
				BKE_mesh_normals_loop_split(verts_dst, num_verts_dst, edges_dst, num_edges_dst,
				                            loops_dst, loop_nors_dst, num_loops_dst,
				                            polys_dst, (const float (*)[3])poly_nors_dst, num_polys_dst,
				                            use_split_nors_dst, split_angle_dst, NULL, custom_nors_dst, NULL);
			}
		}
	}
}
static void rna_Mesh_create_normals_split(Mesh *mesh)
{
	if (!CustomData_has_layer(&mesh->ldata, CD_NORMAL)) {
		CustomData_add_layer(&mesh->ldata, CD_NORMAL, CD_CALLOC, NULL, mesh->totloop);
		CustomData_set_layer_flag(&mesh->ldata, CD_NORMAL, CD_FLAG_TEMPORARY);
	}
}
static void rna_Mesh_calc_tangents(Mesh *mesh, ReportList *reports, const char *uvmap)
{
	float (*r_looptangents)[4];

	if (CustomData_has_layer(&mesh->ldata, CD_MLOOPTANGENT)) {
		r_looptangents = CustomData_get_layer(&mesh->ldata, CD_MLOOPTANGENT);
		memset(r_looptangents, 0, sizeof(float[4]) * mesh->totloop);
	}
	else {
		r_looptangents = CustomData_add_layer(&mesh->ldata, CD_MLOOPTANGENT, CD_CALLOC, NULL, mesh->totloop);
		CustomData_set_layer_flag(&mesh->ldata, CD_MLOOPTANGENT, CD_FLAG_TEMPORARY);
	}

	/* Compute loop normals if needed. */
	if (!CustomData_has_layer(&mesh->ldata, CD_NORMAL)) {
		BKE_mesh_calc_normals_split(mesh);
	}

	BKE_mesh_loop_tangents(mesh, uvmap, r_looptangents, reports);
}