Ejemplo n.º 1
0
Mesh *bc_get_mesh_copy(Scene *scene, Object *ob, BC_export_mesh_type export_mesh_type, bool apply_modifiers, bool triangulate)
{
	Mesh *tmpmesh;
	CustomDataMask mask = CD_MASK_MESH;
	Mesh *mesh = (Mesh *)ob->data;
	DerivedMesh *dm = NULL;
	if (apply_modifiers) {
		switch (export_mesh_type) {
			case BC_MESH_TYPE_VIEW:
			{
				dm = mesh_create_derived_view(scene, ob, mask);
				break;
			}
			case BC_MESH_TYPE_RENDER:
			{
				dm = mesh_create_derived_render(scene, ob, mask);
				break;
			}
		}
	}
	else {
		dm = mesh_create_derived((Mesh *)ob->data, NULL);
	}

	tmpmesh = BKE_mesh_add(G.main, "ColladaMesh"); // name is not important here
	DM_to_mesh(dm, tmpmesh, ob, CD_MASK_MESH, true);
	tmpmesh->flag = mesh->flag;

	if (triangulate) {
		bc_triangulate_mesh(tmpmesh);
	}
	BKE_mesh_tessface_ensure(tmpmesh);
	return tmpmesh;
}
Ejemplo n.º 2
0
static Mesh *applyModifier(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mesh)
{
  ExplodeModifierData *emd = (ExplodeModifierData *)md;
  ParticleSystemModifierData *psmd = findPrecedingParticlesystem(ctx->object, md);

  if (psmd) {
    ParticleSystem *psys = psmd->psys;

    if (psys == NULL || psys->totpart == 0) {
      return mesh;
    }
    if (psys->part == NULL || psys->particles == NULL) {
      return mesh;
    }
    if (psmd->mesh_final == NULL) {
      return mesh;
    }

    BKE_mesh_tessface_ensure(mesh); /* BMESH - UNTIL MODIFIER IS UPDATED FOR MPoly */

    /* 1. find faces to be exploded if needed */
    if (emd->facepa == NULL || psmd->flag & eParticleSystemFlag_Pars ||
        emd->flag & eExplodeFlag_CalcFaces ||
        MEM_allocN_len(emd->facepa) / sizeof(int) != mesh->totface) {
      if (psmd->flag & eParticleSystemFlag_Pars) {
        psmd->flag &= ~eParticleSystemFlag_Pars;
      }
      if (emd->flag & eExplodeFlag_CalcFaces) {
        emd->flag &= ~eExplodeFlag_CalcFaces;
      }
      createFacepa(emd, psmd, mesh);
    }
    /* 2. create new mesh */
    Scene *scene = DEG_get_evaluated_scene(ctx->depsgraph);
    if (emd->flag & eExplodeFlag_EdgeCut) {
      int *facepa = emd->facepa;
      Mesh *split_m = cutEdges(emd, mesh);
      Mesh *explode = explodeMesh(emd, psmd, ctx, scene, split_m);

      MEM_freeN(emd->facepa);
      emd->facepa = facepa;
      BKE_id_free(NULL, split_m);
      return explode;
    }
    else {
      return explodeMesh(emd, psmd, ctx, scene, mesh);
    }
  }
  return mesh;
}
Ejemplo n.º 3
0
bool ImagesExporter::hasImages(Scene *sce)
{
	LinkNode *node;
	
	for (node = this->export_settings->export_set; node; node = node->next) {
		Object *ob = (Object *)node->link;
		int a;
		for (a = 0; a < ob->totcol; a++) {
			Material *ma = give_current_material(ob, a + 1);

			// no material, but check all of the slots
			if (!ma) continue;
			int b;
			for (b = 0; b < MAX_MTEX; b++) {
				MTex *mtex = ma->mtex[b];
				if (mtex && mtex->tex && mtex->tex->ima) return true;
			}

		}
		if (ob->type == OB_MESH) {
			Mesh *me     = (Mesh *) ob->data;
			BKE_mesh_tessface_ensure(me);
			bool has_uvs = (bool)CustomData_has_layer(&me->fdata, CD_MTFACE);
			if (has_uvs) {
				int num_layers = CustomData_number_of_layers(&me->fdata, CD_MTFACE);
				for (int a = 0; a < num_layers; a++) {
					MTFace *tface = (MTFace *)CustomData_get_layer_n(&me->fdata, CD_MTFACE, a);
					Image *img = tface->tpage;
					if (img) return true;
				}
			}
		}

	}
	return false;
}
Ejemplo n.º 4
0
void ImagesExporter::export_UV_Images()
{
	std::set<Image *> uv_textures;
	LinkNode *node;
	bool use_texture_copies = this->export_settings->use_texture_copies;
	bool active_uv_only     = this->export_settings->active_uv_only;

	for (node = this->export_settings->export_set; node; node = node->next) {
		Object *ob = (Object *)node->link;
		if (ob->type == OB_MESH && ob->totcol) {
			Mesh *me     = (Mesh *) ob->data;
			BKE_mesh_tessface_ensure(me);
			int active_uv_layer = CustomData_get_active_layer_index(&me->pdata, CD_MTEXPOLY);
			for (int i = 0; i < me->pdata.totlayer; i++) {
				if (me->pdata.layers[i].type == CD_MTEXPOLY) {
					if (!active_uv_only || active_uv_layer == i)
					{
						MTexPoly *txface = (MTexPoly *)me->pdata.layers[i].data;
						for (int j = 0; j < me->totpoly; j++, txface++) {

							Image *ima = txface->tpage;
							if (ima == NULL)
								continue;

							bool not_in_list = uv_textures.find(ima) == uv_textures.end();
							if (not_in_list) {
									uv_textures.insert(ima);
									export_UV_Image(ima, use_texture_copies);
							}
						}
					}
				}
			}
		}
	}
}
Ejemplo n.º 5
0
void EffectsExporter::operator()(Material *ma, Object *ob)
{
	// create a list of indices to textures of type TEX_IMAGE
	std::vector<int> tex_indices;
	if (this->export_settings->include_material_textures)
		createTextureIndices(ma, tex_indices);

	openEffect(translate_id(id_name(ma)) + "-effect");
	
	COLLADASW::EffectProfile ep(mSW);
	ep.setProfileType(COLLADASW::EffectProfile::COMMON);
	ep.openProfile();
	// set shader type - one of three blinn, phong or lambert
	if (ma->spec > 0.0f) {
		if (ma->spec_shader == MA_SPEC_BLINN) {
			writeBlinn(ep, ma);
		}
		else {
			// \todo figure out handling of all spec+diff shader combos blender has, for now write phong
			// for now set phong in case spec shader is not blinn
			writePhong(ep, ma);
		}
	}
	else {
		if (ma->diff_shader == MA_DIFF_LAMBERT) {
			writeLambert(ep, ma);
		}
		else {
			// \todo figure out handling of all spec+diff shader combos blender has, for now write phong
			writePhong(ep, ma);
		}
	}
	
	// index of refraction
	if (ma->mode & MA_RAYTRANSP) {
		ep.setIndexOfRefraction(ma->ang, false, "index_of_refraction");
	}
	else {
		ep.setIndexOfRefraction(1.0f, false, "index_of_refraction");
	}

	COLLADASW::ColorOrTexture cot;

	// transparency
	if (ma->mode & MA_TRANSP) {
		// Tod: because we are in A_ONE mode transparency is calculated like this:
		ep.setTransparency(ma->alpha, false, "transparency");
		// cot = getcol(1.0f, 1.0f, 1.0f, 1.0f);
		// ep.setTransparent(cot);
	}

	// emission
	cot = getcol(ma->emit, ma->emit, ma->emit, 1.0f);
	ep.setEmission(cot, false, "emission");

	// diffuse multiplied by diffuse intensity
	cot = getcol(ma->r * ma->ref, ma->g * ma->ref, ma->b * ma->ref, 1.0f);
	ep.setDiffuse(cot, false, "diffuse");

	// ambient
	/* ma->ambX is calculated only on render, so lets do it here manually and not rely on ma->ambX. */
	if (this->scene->world)
		cot = getcol(this->scene->world->ambr * ma->amb, this->scene->world->ambg * ma->amb, this->scene->world->ambb * ma->amb, 1.0f);
	else
		cot = getcol(ma->amb, ma->amb, ma->amb, 1.0f);

	ep.setAmbient(cot, false, "ambient");

	// reflective, reflectivity
	if (ma->mode & MA_RAYMIRROR) {
		cot = getcol(ma->mirr, ma->mirg, ma->mirb, 1.0f);
		ep.setReflective(cot);
		ep.setReflectivity(ma->ray_mirror);
	}
	// else {
	//  cot = getcol(ma->specr, ma->specg, ma->specb, 1.0f);
	//  ep.setReflective(cot);
	//  ep.setReflectivity(ma->spec);
	// }

	// specular
	if (ep.getShaderType() != COLLADASW::EffectProfile::LAMBERT) {
		cot = getcol(ma->specr * ma->spec, ma->specg * ma->spec, ma->specb * ma->spec, 1.0f);
		ep.setSpecular(cot, false, "specular");
	}

	// XXX make this more readable if possible

	// create <sampler> and <surface> for each image
	COLLADASW::Sampler samplers[MAX_MTEX];
	//COLLADASW::Surface surfaces[MAX_MTEX];
	//void *samp_surf[MAX_MTEX][2];
	void *samp_surf[MAX_MTEX][1];
	
	// image to index to samp_surf map
	// samp_surf[index] stores 2 pointers, sampler and surface
	std::map<std::string, int> im_samp_map;

	unsigned int a, b;
	for (a = 0, b = 0; a < tex_indices.size(); a++) {
		MTex *t = ma->mtex[tex_indices[a]];
		Image *ima = t->tex->ima;
		
		// Image not set for texture
		if (!ima) continue;
		
		std::string key(id_name(ima));
		key = translate_id(key);

		// create only one <sampler>/<surface> pair for each unique image
		if (im_samp_map.find(key) == im_samp_map.end()) {
			// //<newparam> <surface> <init_from>
			// COLLADASW::Surface surface(COLLADASW::Surface::SURFACE_TYPE_2D,
			//                         key + COLLADASW::Surface::SURFACE_SID_SUFFIX);
			// COLLADASW::SurfaceInitOption sio(COLLADASW::SurfaceInitOption::INIT_FROM);
			// sio.setImageReference(key);
			// surface.setInitOption(sio);

			// COLLADASW::NewParamSurface surface(mSW);
			// surface->setParamType(COLLADASW::CSW_SURFACE_TYPE_2D);
			
			//<newparam> <sampler> <source>
			COLLADASW::Sampler sampler(COLLADASW::Sampler::SAMPLER_TYPE_2D,
			                           key + COLLADASW::Sampler::SAMPLER_SID_SUFFIX,
			                           key + COLLADASW::Sampler::SURFACE_SID_SUFFIX);
			sampler.setImageId(key);
			// copy values to arrays since they will live longer
			samplers[a] = sampler;
			//surfaces[a] = surface;
			
			// store pointers so they can be used later when we create <texture>s
			samp_surf[b][0] = &samplers[a];
			//samp_surf[b][1] = &surfaces[a];
			
			im_samp_map[key] = b;
			b++;
		}
	}


	std::set<Image *> uv_textures;
	if (ob->type == OB_MESH && ob->totcol && this->export_settings->include_uv_textures) {
		bool active_uv_only = this->export_settings->active_uv_only;
		Mesh *me     = (Mesh *) ob->data;
		int active_uv_layer = CustomData_get_active_layer_index(&me->pdata, CD_MTEXPOLY);

		BKE_mesh_tessface_ensure(me);
		for (int i = 0; i < me->pdata.totlayer; i++) {
			if (!active_uv_only || active_uv_layer == i)
			{
				if (me->pdata.layers[i].type == CD_MTEXPOLY) {
					MTexPoly *txface = (MTexPoly *)me->pdata.layers[i].data;
					MFace *mface = me->mface;
					for (int j = 0; j < me->totpoly; j++, mface++, txface++) {

						Material *mat = give_current_material(ob, mface->mat_nr + 1);
						if (mat != ma) 
							continue;

						Image *ima = txface->tpage;
						if (ima == NULL)
							continue;


						bool not_in_list = uv_textures.find(ima)==uv_textures.end();
						if (not_in_list) {
							std::string name = id_name(ima);
							std::string key(name);
							key = translate_id(key);

							// create only one <sampler>/<surface> pair for each unique image
							if (im_samp_map.find(key) == im_samp_map.end()) {
								//<newparam> <sampler> <source>
								COLLADASW::Sampler sampler(COLLADASW::Sampler::SAMPLER_TYPE_2D,
														   key + COLLADASW::Sampler::SAMPLER_SID_SUFFIX,
														   key + COLLADASW::Sampler::SURFACE_SID_SUFFIX);
								sampler.setImageId(key);
								samplers[a] = sampler;
								samp_surf[b][0] = &samplers[a];
								im_samp_map[key] = b;
								b++;
								a++;
								uv_textures.insert(ima);
							}
						}
					}
				}
			}
		}
	}

	// used as fallback when MTex->uvname is "" (this is pretty common)
	// it is indeed the correct value to use in that case
	std::string active_uv(getActiveUVLayerName(ob));

	// write textures
	// XXX very slow
	for (a = 0; a < tex_indices.size(); a++) {
		MTex *t = ma->mtex[tex_indices[a]];
		Image *ima = t->tex->ima;

		std::string key(id_name(ima));
		key = translate_id(key);
		int i = im_samp_map[key];
		std::string uvname = strlen(t->uvname) ? t->uvname : active_uv;
		COLLADASW::Sampler *sampler = (COLLADASW::Sampler *)samp_surf[i][0];
		writeTextures(ep, key, sampler, t, ima, uvname);
	}

	std::set<Image *>::iterator uv_t_iter;
	for (uv_t_iter = uv_textures.begin(); uv_t_iter != uv_textures.end(); uv_t_iter++ ) {
		Image *ima = *uv_t_iter;
		std::string key(id_name(ima));
		key = translate_id(key);
		int i = im_samp_map[key];
		COLLADASW::Sampler *sampler = (COLLADASW::Sampler *)samp_surf[i][0];
		ep.setDiffuse(createTexture(ima, active_uv, sampler), false, "diffuse");
	}

	// performs the actual writing
	ep.addProfileElements();
	bool twoSided = false;
	if (ob->type == OB_MESH && ob->data) {
		Mesh *me = (Mesh *)ob->data;
		if (me->flag & ME_TWOSIDED)
			twoSided = true;
	}
	if (twoSided)
		ep.addExtraTechniqueParameter("GOOGLEEARTH", "double_sided", 1);
	ep.addExtraTechniques(mSW);

	ep.closeProfile();
	if (twoSided)
		mSW->appendTextBlock("<extra><technique profile=\"MAX3D\"><double_sided>1</double_sided></technique></extra>");
	closeEffect();
}
Ejemplo n.º 6
0
static int bake(
        Render *re, Main *bmain, Scene *scene, Object *ob_low, ListBase *selected_objects, ReportList *reports,
        const ScenePassType pass_type, const int margin,
        const BakeSaveMode save_mode, const bool is_clear, const bool is_split_materials,
        const bool is_automatic_name, const bool is_selected_to_active, const bool is_cage,
        const float cage_extrusion, const int normal_space, const BakeNormalSwizzle normal_swizzle[],
        const char *custom_cage, const char *filepath, const int width, const int height,
        const char *identifier, ScrArea *sa, const char *uv_layer)
{
	int op_result = OPERATOR_CANCELLED;
	bool ok = false;

	Object *ob_cage = NULL;

	BakeHighPolyData *highpoly = NULL;
	int tot_highpoly = 0;

	char restrict_flag_low = ob_low->restrictflag;
	char restrict_flag_cage = 0;

	Mesh *me_low = NULL;
	Mesh *me_cage = NULL;

	float *result = NULL;

	BakePixel *pixel_array_low = NULL;
	BakePixel *pixel_array_high = NULL;

	const bool is_save_internal = (save_mode == R_BAKE_SAVE_INTERNAL);
	const bool is_noncolor = is_noncolor_pass(pass_type);
	const int depth = RE_pass_depth(pass_type);

	BakeImages bake_images = {NULL};

	size_t num_pixels;
	int tot_materials;
	int i;

	RE_bake_engine_set_engine_parameters(re, bmain, scene);

	if (!RE_bake_has_engine(re)) {
		BKE_report(reports, RPT_ERROR, "Current render engine does not support baking");
		goto cleanup;
	}

	tot_materials = ob_low->totcol;

	if (uv_layer && uv_layer[0] != '\0') {
		Mesh *me = (Mesh *)ob_low->data;
		if (CustomData_get_named_layer(&me->ldata, CD_MLOOPUV, uv_layer) == -1) {
			BKE_reportf(reports, RPT_ERROR,
			            "No UV layer named \"%s\" found in the object \"%s\"", uv_layer, ob_low->id.name + 2);
			goto cleanup;
		}
	}

	if (tot_materials == 0) {
		if (is_save_internal) {
			BKE_report(reports, RPT_ERROR,
			           "No active image found, add a material or bake to an external file");

			goto cleanup;
		}
		else if (is_split_materials) {
			BKE_report(reports, RPT_ERROR,
			           "No active image found, add a material or bake without the Split Materials option");

			goto cleanup;
		}
		else {
			/* baking externally without splitting materials */
			tot_materials = 1;
		}
	}

	/* we overallocate in case there is more materials than images */
	bake_images.data = MEM_mallocN(sizeof(BakeImage) * tot_materials, "bake images dimensions (width, height, offset)");
	bake_images.lookup = MEM_mallocN(sizeof(int) * tot_materials, "bake images lookup (from material to BakeImage)");

	build_image_lookup(bmain, ob_low, &bake_images);

	if (is_save_internal) {
		num_pixels = initialize_internal_images(&bake_images, reports);

		if (num_pixels == 0) {
			goto cleanup;
		}
	}
	else {
		/* when saving extenally always use the size specified in the UI */

		num_pixels = (size_t)width * (size_t)height * bake_images.size;

		for (i = 0; i < bake_images.size; i++) {
			bake_images.data[i].width = width;
			bake_images.data[i].height = height;
			bake_images.data[i].offset = (is_split_materials ? num_pixels : 0);
			bake_images.data[i].image = NULL;
		}

		if (!is_split_materials) {
			/* saving a single image */
			for (i = 0; i < tot_materials; i++)
				bake_images.lookup[i] = 0;
		}
	}

	if (is_selected_to_active) {
		CollectionPointerLink *link;
		tot_highpoly = 0;

		for (link = selected_objects->first; link; link = link->next) {
			Object *ob_iter = link->ptr.data;

			if (ob_iter == ob_low)
				continue;

			tot_highpoly ++;
		}

		if (is_cage && custom_cage[0] != '\0') {
			ob_cage = BLI_findstring(&bmain->object, custom_cage, offsetof(ID, name) + 2);

			if (ob_cage == NULL || ob_cage->type != OB_MESH) {
				BKE_report(reports, RPT_ERROR, "No valid cage object");
				goto cleanup;
			}
			else {
				restrict_flag_cage = ob_cage->restrictflag;
				ob_cage->restrictflag |= OB_RESTRICT_RENDER;
			}
		}
	}

	pixel_array_low = MEM_mallocN(sizeof(BakePixel) * num_pixels, "bake pixels low poly");
	pixel_array_high = MEM_mallocN(sizeof(BakePixel) * num_pixels, "bake pixels high poly");
	result = MEM_callocN(sizeof(float) * depth * num_pixels, "bake return pixels");

	/* get the mesh as it arrives in the renderer */
	me_low = BKE_mesh_new_from_object(bmain, scene, ob_low, 1, 2, 0, 0);
	BKE_mesh_split_faces(me_low);
	BKE_mesh_tessface_ensure(me_low);

	/* populate the pixel array with the face data */
	if ((is_selected_to_active && (ob_cage == NULL) && is_cage) == false)
		RE_bake_pixels_populate(me_low, pixel_array_low, num_pixels, &bake_images, uv_layer);
	/* else populate the pixel array with the 'cage' mesh (the smooth version of the mesh)  */

	if (is_selected_to_active) {
		CollectionPointerLink *link;
		ModifierData *md, *nmd;
		ListBase modifiers_tmp, modifiers_original;
		int i = 0;

		/* prepare cage mesh */
		if (ob_cage) {
			me_cage = BKE_mesh_new_from_object(bmain, scene, ob_cage, 1, 2, 0, 0);
			BKE_mesh_split_faces(me_cage);
			BKE_mesh_tessface_ensure(me_cage);
			if (me_low->totface != me_cage->totface) {
				BKE_report(reports, RPT_ERROR,
				           "Invalid cage object, the cage mesh must have the same number "
				           "of faces as the active object");
				goto cleanup;
			}
		}
		else if (is_cage) {
			modifiers_original = ob_low->modifiers;
			BLI_listbase_clear(&modifiers_tmp);

			for (md = ob_low->modifiers.first; md; md = md->next) {
				/* Edge Split cannot be applied in the cage,
				 * the cage is supposed to have interpolated normals
				 * between the faces unless the geometry is physically
				 * split. So we create a copy of the low poly mesh without
				 * the eventual edge split.*/

				if (md->type == eModifierType_EdgeSplit)
					continue;

				nmd = modifier_new(md->type);
				BLI_strncpy(nmd->name, md->name, sizeof(nmd->name));
				modifier_copyData(md, nmd);
				BLI_addtail(&modifiers_tmp, nmd);
			}

			/* temporarily replace the modifiers */
			ob_low->modifiers = modifiers_tmp;

			/* get the cage mesh as it arrives in the renderer */
			me_cage = BKE_mesh_new_from_object(bmain, scene, ob_low, 1, 2, 0, 0);
			BKE_mesh_split_faces(me_cage);
			BKE_mesh_tessface_ensure(me_cage);
			RE_bake_pixels_populate(me_cage, pixel_array_low, num_pixels, &bake_images, uv_layer);
		}

		highpoly = MEM_callocN(sizeof(BakeHighPolyData) * tot_highpoly, "bake high poly objects");

		/* populate highpoly array */
		for (link = selected_objects->first; link; link = link->next) {
			TriangulateModifierData *tmd;
			Object *ob_iter = link->ptr.data;

			if (ob_iter == ob_low)
				continue;

			/* initialize highpoly_data */
			highpoly[i].ob = ob_iter;
			highpoly[i].restrict_flag = ob_iter->restrictflag;

			/* triangulating so BVH returns the primitive_id that will be used for rendering */
			highpoly[i].tri_mod = ED_object_modifier_add(
			        reports, bmain, scene, highpoly[i].ob,
			        "TmpTriangulate", eModifierType_Triangulate);
			tmd = (TriangulateModifierData *)highpoly[i].tri_mod;
			tmd->quad_method = MOD_TRIANGULATE_QUAD_FIXED;
			tmd->ngon_method = MOD_TRIANGULATE_NGON_EARCLIP;

			highpoly[i].me = BKE_mesh_new_from_object(bmain, scene, highpoly[i].ob, 1, 2, 0, 0);
			highpoly[i].ob->restrictflag &= ~OB_RESTRICT_RENDER;
			BKE_mesh_split_faces(highpoly[i].me);
			BKE_mesh_tessface_ensure(highpoly[i].me);

			/* lowpoly to highpoly transformation matrix */
			copy_m4_m4(highpoly[i].obmat, highpoly[i].ob->obmat);
			invert_m4_m4(highpoly[i].imat, highpoly[i].obmat);

			highpoly[i].is_flip_object = is_negative_m4(highpoly[i].ob->obmat);

			i++;
		}

		BLI_assert(i == tot_highpoly);

		ob_low->restrictflag |= OB_RESTRICT_RENDER;

		/* populate the pixel arrays with the corresponding face data for each high poly object */
		if (!RE_bake_pixels_populate_from_objects(
		            me_low, pixel_array_low, pixel_array_high, highpoly, tot_highpoly, num_pixels, ob_cage != NULL,
		            cage_extrusion, ob_low->obmat, (ob_cage ? ob_cage->obmat : ob_low->obmat), me_cage))
		{
			BKE_report(reports, RPT_ERROR, "Error handling selected objects");
			goto cage_cleanup;
		}

		/* the baking itself */
		for (i = 0; i < tot_highpoly; i++) {
			ok = RE_bake_engine(re, highpoly[i].ob, i, pixel_array_high,
			                    num_pixels, depth, pass_type, result);
			if (!ok) {
				BKE_reportf(reports, RPT_ERROR, "Error baking from object \"%s\"", highpoly[i].ob->id.name + 2);
				goto cage_cleanup;
			}
		}

cage_cleanup:
		/* reverting data back */
		if ((ob_cage == NULL) && is_cage) {
			ob_low->modifiers = modifiers_original;

			while ((md = BLI_pophead(&modifiers_tmp))) {
				modifier_free(md);
			}
		}

		if (!ok) {
			goto cleanup;
		}
	}
	else {
		/* make sure low poly renders */
		ob_low->restrictflag &= ~OB_RESTRICT_RENDER;

		if (RE_bake_has_engine(re)) {
			ok = RE_bake_engine(re, ob_low, 0, pixel_array_low, num_pixels, depth, pass_type, result);
		}
		else {
			BKE_report(reports, RPT_ERROR, "Current render engine does not support baking");
			goto cleanup;
		}
	}

	/* normal space conversion
	 * the normals are expected to be in world space, +X +Y +Z */
	if (ok && pass_type == SCE_PASS_NORMAL) {
		switch (normal_space) {
			case R_BAKE_SPACE_WORLD:
			{
				/* Cycles internal format */
				if ((normal_swizzle[0] == R_BAKE_POSX) &&
				    (normal_swizzle[1] == R_BAKE_POSY) &&
				    (normal_swizzle[2] == R_BAKE_POSZ))
				{
					break;
				}
				else {
					RE_bake_normal_world_to_world(pixel_array_low, num_pixels,  depth, result, normal_swizzle);
				}
				break;
			}
			case R_BAKE_SPACE_OBJECT:
			{
				RE_bake_normal_world_to_object(pixel_array_low, num_pixels, depth, result, ob_low, normal_swizzle);
				break;
			}
			case R_BAKE_SPACE_TANGENT:
			{
				if (is_selected_to_active) {
					RE_bake_normal_world_to_tangent(pixel_array_low, num_pixels, depth, result, me_low, normal_swizzle, ob_low->obmat);
				}
				else {
					/* from multiresolution */
					Mesh *me_nores = NULL;
					ModifierData *md = NULL;
					int mode;

					md = modifiers_findByType(ob_low, eModifierType_Multires);

					if (md) {
						mode = md->mode;
						md->mode &= ~eModifierMode_Render;
					}

					me_nores = BKE_mesh_new_from_object(bmain, scene, ob_low, 1, 2, 0, 0);
					BKE_mesh_split_faces(me_nores);
					BKE_mesh_tessface_ensure(me_nores);
					RE_bake_pixels_populate(me_nores, pixel_array_low, num_pixels, &bake_images, uv_layer);

					RE_bake_normal_world_to_tangent(pixel_array_low, num_pixels, depth, result, me_nores, normal_swizzle, ob_low->obmat);
					BKE_libblock_free(bmain, me_nores);

					if (md)
						md->mode = mode;
				}
				break;
			}
			default:
				break;
		}
	}

	if (!ok) {
		BKE_reportf(reports, RPT_ERROR, "Problem baking object \"%s\"", ob_low->id.name + 2);
		op_result = OPERATOR_CANCELLED;
	}
	else {
		/* save the results */
		for (i = 0; i < bake_images.size; i++) {
			BakeImage *bk_image = &bake_images.data[i];

			if (is_save_internal) {
				ok = write_internal_bake_pixels(
				         bk_image->image,
				         pixel_array_low + bk_image->offset,
				         result + bk_image->offset * depth,
				         bk_image->width, bk_image->height,
				         margin, is_clear, is_noncolor);

				/* might be read by UI to set active image for display */
				bake_update_image(sa, bk_image->image);

				if (!ok) {
					BKE_reportf(reports, RPT_ERROR,
					           "Problem saving the bake map internally for object \"%s\"", ob_low->id.name + 2);
					op_result = OPERATOR_CANCELLED;
				}
				else {
					BKE_report(reports, RPT_INFO,
					           "Baking map saved to internal image, save it externally or pack it");
					op_result = OPERATOR_FINISHED;
				}
			}
			/* save externally */
			else {
				BakeData *bake = &scene->r.bake;
				char name[FILE_MAX];

				BKE_image_path_from_imtype(name, filepath, bmain->name, 0, bake->im_format.imtype, true, false, NULL);

				if (is_automatic_name) {
					BLI_path_suffix(name, FILE_MAX, ob_low->id.name + 2, "_");
					BLI_path_suffix(name, FILE_MAX, identifier, "_");
				}

				if (is_split_materials) {
					if (bk_image->image) {
						BLI_path_suffix(name, FILE_MAX, bk_image->image->id.name + 2, "_");
					}
					else {
						if (ob_low->mat[i]) {
							BLI_path_suffix(name, FILE_MAX, ob_low->mat[i]->id.name + 2, "_");
						}
						else if (me_low->mat[i]) {
							BLI_path_suffix(name, FILE_MAX, me_low->mat[i]->id.name + 2, "_");
						}
						else {
							/* if everything else fails, use the material index */
							char tmp[4];
							sprintf(tmp, "%d", i % 1000);
							BLI_path_suffix(name, FILE_MAX, tmp, "_");
						}
					}
				}

				/* save it externally */
				ok = write_external_bake_pixels(
				        name,
				        pixel_array_low + bk_image->offset,
				        result + bk_image->offset * depth,
				        bk_image->width, bk_image->height,
				        margin, &bake->im_format, is_noncolor);

				if (!ok) {
					BKE_reportf(reports, RPT_ERROR, "Problem saving baked map in \"%s\"", name);
					op_result = OPERATOR_CANCELLED;
				}
				else {
					BKE_reportf(reports, RPT_INFO, "Baking map written to \"%s\"", name);
					op_result = OPERATOR_FINISHED;
				}

				if (!is_split_materials) {
					break;
				}
			}
		}
	}

	if (is_save_internal)
		refresh_images(&bake_images);

cleanup:

	if (highpoly) {
		int i;
		for (i = 0; i < tot_highpoly; i++) {
			highpoly[i].ob->restrictflag = highpoly[i].restrict_flag;

			if (highpoly[i].tri_mod)
				ED_object_modifier_remove(reports, bmain, highpoly[i].ob, highpoly[i].tri_mod);

			if (highpoly[i].me)
				BKE_libblock_free(bmain, highpoly[i].me);
		}
		MEM_freeN(highpoly);
	}

	ob_low->restrictflag = restrict_flag_low;

	if (ob_cage)
		ob_cage->restrictflag = restrict_flag_cage;

	if (pixel_array_low)
		MEM_freeN(pixel_array_low);

	if (pixel_array_high)
		MEM_freeN(pixel_array_high);

	if (bake_images.data)
		MEM_freeN(bake_images.data);

	if (bake_images.lookup)
		MEM_freeN(bake_images.lookup);

	if (result)
		MEM_freeN(result);

	if (me_low)
		BKE_libblock_free(bmain, me_low);

	if (me_cage)
		BKE_libblock_free(bmain, me_cage);

	return op_result;
}