Browse Source

Merge pull request #48416 from JFonS/fix_gpu_lightmapper

Assorted fixes to UV unwrapping and GPU lightmapper
Rémi Verschelde 4 years ago
parent
commit
ea5d2089d9

+ 33 - 116
editor/import/resource_importer_scene.cpp

@@ -1136,7 +1136,7 @@ Ref<Animation> ResourceImporterScene::import_animation_from_other_importer(Edito
 	return importer->import_animation(p_path, p_flags, p_bake_fps);
 }
 
-void ResourceImporterScene::_generate_meshes(Node *p_node, const Dictionary &p_mesh_data, bool p_generate_lods, bool p_create_shadow_meshes, LightBakeMode p_light_bake_mode, float p_lightmap_texel_size, const Vector<uint8_t> &p_src_lightmap_cache, Vector<uint8_t> &r_dst_lightmap_cache) {
+void ResourceImporterScene::_generate_meshes(Node *p_node, const Dictionary &p_mesh_data, bool p_generate_lods, bool p_create_shadow_meshes, LightBakeMode p_light_bake_mode, float p_lightmap_texel_size, const Vector<uint8_t> &p_src_lightmap_cache, Vector<Vector<uint8_t>> &r_lightmap_caches) {
 	EditorSceneImporterMeshNode3D *src_mesh_node = Object::cast_to<EditorSceneImporterMeshNode3D>(p_node);
 	if (src_mesh_node) {
 		//is mesh
@@ -1216,7 +1216,28 @@ void ResourceImporterScene::_generate_meshes(Node *p_node, const Dictionary &p_m
 						n = n->get_parent_spatial();
 					}
 
-					//use xf as transform for mesh, and bake it
+					Vector<uint8_t> lightmap_cache;
+					src_mesh_node->get_mesh()->lightmap_unwrap_cached(xf, p_lightmap_texel_size, p_src_lightmap_cache, lightmap_cache);
+
+					if (!lightmap_cache.is_empty()) {
+						if (r_lightmap_caches.is_empty()) {
+							r_lightmap_caches.push_back(lightmap_cache);
+						} else {
+							String new_md5 = String::md5(lightmap_cache.ptr()); // MD5 is stored at the beginning of the cache data
+
+							for (int i = 0; i < r_lightmap_caches.size(); i++) {
+								String md5 = String::md5(r_lightmap_caches[i].ptr());
+								if (new_md5 < md5) {
+									r_lightmap_caches.insert(i, lightmap_cache);
+									break;
+								}
+
+								if (new_md5 == md5) {
+									break;
+								}
+							}
+						}
+					}
 				}
 
 				if (save_to_file != String()) {
@@ -1265,7 +1286,7 @@ void ResourceImporterScene::_generate_meshes(Node *p_node, const Dictionary &p_m
 	}
 
 	for (int i = 0; i < p_node->get_child_count(); i++) {
-		_generate_meshes(p_node->get_child(i), p_mesh_data, p_generate_lods, p_create_shadow_meshes, p_light_bake_mode, p_lightmap_texel_size, p_src_lightmap_cache, r_dst_lightmap_cache);
+		_generate_meshes(p_node->get_child(i), p_mesh_data, p_generate_lods, p_create_shadow_meshes, p_light_bake_mode, p_lightmap_texel_size, p_src_lightmap_cache, r_lightmap_caches);
 	}
 }
 
@@ -1433,7 +1454,7 @@ Error ResourceImporterScene::import(const String &p_source_file, const String &p
 	float lightmap_texel_size = MAX(0.001, texel_size);
 
 	Vector<uint8_t> src_lightmap_cache;
-	Vector<uint8_t> dst_lightmap_cache;
+	Vector<Vector<uint8_t>> mesh_lightmap_caches;
 
 	{
 		src_lightmap_cache = FileAccess::get_file_as_array(p_source_file + ".unwrap_cache", &err);
@@ -1446,124 +1467,20 @@ Error ResourceImporterScene::import(const String &p_source_file, const String &p
 	if (subresources.has("meshes")) {
 		mesh_data = subresources["meshes"];
 	}
-	_generate_meshes(scene, mesh_data, gen_lods, create_shadow_meshes, LightBakeMode(light_bake_mode), lightmap_texel_size, src_lightmap_cache, dst_lightmap_cache);
+	_generate_meshes(scene, mesh_data, gen_lods, create_shadow_meshes, LightBakeMode(light_bake_mode), lightmap_texel_size, src_lightmap_cache, mesh_lightmap_caches);
 
-	if (dst_lightmap_cache.size()) {
+	if (mesh_lightmap_caches.size()) {
 		FileAccessRef f = FileAccess::open(p_source_file + ".unwrap_cache", FileAccess::WRITE);
 		if (f) {
-			f->store_buffer(dst_lightmap_cache.ptr(), dst_lightmap_cache.size());
-		}
-	}
-	err = OK;
-
-#if 0
-	if (light_bake_mode == 2 /* || generate LOD */) {
-		Map<Ref<ArrayMesh>, Transform> meshes;
-		_find_meshes(scene, meshes);
-
-		String file_id = src_path.get_file();
-		String cache_file_path = base_path.plus_file(file_id + ".unwrap_cache");
-
-		Vector<unsigned char> cache_data;
-
-		if (FileAccess::exists(cache_file_path)) {
-			Error err2;
-			FileAccess *file = FileAccess::open(cache_file_path, FileAccess::READ, &err2);
-
-			if (err2) {
-				if (file) {
-					memdelete(file);
-				}
-			} else {
-				int cache_size = file->get_len();
-				cache_data.resize(cache_size);
-				file->get_buffer(cache_data.ptrw(), cache_size);
+			f->store_32(mesh_lightmap_caches.size());
+			for (int i = 0; i < mesh_lightmap_caches.size(); i++) {
+				String md5 = String::md5(mesh_lightmap_caches[i].ptr());
+				f->store_buffer(mesh_lightmap_caches[i].ptr(), mesh_lightmap_caches[i].size());
 			}
-		}
-
-		Map<String, unsigned int> used_unwraps;
-
-		EditorProgress progress2("gen_lightmaps", TTR("Generating Lightmaps"), meshes.size());
-		int step = 0;
-		for (Map<Ref<ArrayMesh>, Transform>::Element *E = meshes.front(); E; E = E->next()) {
-			Ref<ArrayMesh> mesh = E->key();
-			String name = mesh->get_name();
-			if (name == "") { //should not happen but..
-				name = "Mesh " + itos(step);
-			}
-
-			progress2.step(TTR("Generating for Mesh: ") + name + " (" + itos(step) + "/" + itos(meshes.size()) + ")", step);
-
-			int *ret_cache_data = (int *)cache_data.ptrw();
-			unsigned int ret_cache_size = cache_data.size();
-			bool ret_used_cache = true; // Tell the unwrapper to use the cache
-			Error err2 = mesh->lightmap_unwrap_cached(ret_cache_data, ret_cache_size, ret_used_cache, E->get(), texel_size);
-
-			if (err2 != OK) {
-				EditorNode::add_io_error("Mesh '" + name + "' failed lightmap generation. Please fix geometry.");
-			} else {
-`				String hash = String::md5((unsigned char *)ret_cache_data);
-				used_unwraps.insert(hash, ret_cache_size);
-
-				if (!ret_used_cache) {
-					// Cache was not used, add the generated entry to the current cache
-					if (cache_data.is_empty()) {
-						cache_data.resize(4 + ret_cache_size);
-						int *data = (int *)cache_data.ptrw();
-						data[0] = 1;
-						memcpy(&data[1], ret_cache_data, ret_cache_size);
-					} else {
-						int current_size = cache_data.size();
-						cache_data.resize(cache_data.size() + ret_cache_size);
-							unsigned char *ptrw = cache_data.ptrw();
-						memcpy(&ptrw[current_size], ret_cache_data, ret_cache_size);
-						int *data = (int *)ptrw;
-						data[0] += 1;
-					}
-				}
-			}
-			step++;
-		}
-
-		Error err2;
-		FileAccess *file = FileAccess::open(cache_file_path, FileAccess::WRITE, &err2);
-
-		if (err2) {
-			if (file) {
-				memdelete(file);
-			}
-		} else {
-			// Store number of entries
-			file->store_32(used_unwraps.size());
-
-			// Store cache entries
-			const int *cache = (int *)cache_data.ptr();
-			unsigned int r_idx = 1;
-			for (int i = 0; i < cache[0]; ++i) {
-				unsigned char *entry_start = (unsigned char *)&cache[r_idx];
-				String entry_hash = String::md5(entry_start);
-				if (used_unwraps.has(entry_hash)) {
-					unsigned int entry_size = used_unwraps[entry_hash];
-					file->store_buffer(entry_start, entry_size);
-				}
-
-				r_idx += 4; // hash
-				r_idx += 2; // size hint
-
-				int vertex_count = cache[r_idx];
-				r_idx += 1; // vertex count
-				r_idx += vertex_count; // vertex
-				r_idx += vertex_count * 2; // uvs
-
-				int index_count = cache[r_idx];
-				r_idx += 1; // index count
-				r_idx += index_count; // indices
-			}
-
-			file->close();
+			f->close();
 		}
 	}
-#endif
+	err = OK;
 
 	progress.step(TTR("Running Custom Script..."), 2);
 

+ 1 - 1
editor/import/resource_importer_scene.h

@@ -119,7 +119,7 @@ class ResourceImporterScene : public ResourceImporter {
 	};
 
 	void _replace_owner(Node *p_node, Node *p_scene, Node *p_new_owner);
-	void _generate_meshes(Node *p_node, const Dictionary &p_mesh_data, bool p_generate_lods, bool p_create_shadow_meshes, LightBakeMode p_light_bake_mode, float p_lightmap_texel_size, const Vector<uint8_t> &p_src_lightmap_cache, Vector<uint8_t> &r_dst_lightmap_cache);
+	void _generate_meshes(Node *p_node, const Dictionary &p_mesh_data, bool p_generate_lods, bool p_create_shadow_meshes, LightBakeMode p_light_bake_mode, float p_lightmap_texel_size, const Vector<uint8_t> &p_src_lightmap_cache, Vector<Vector<uint8_t>> &r_lightmap_caches);
 	void _add_shapes(Node *p_node, const List<Ref<Shape3D>> &p_shapes);
 
 public:

+ 72 - 58
editor/import/scene_importer_mesh.cpp

@@ -583,7 +583,7 @@ Ref<NavigationMesh> EditorSceneImporterMesh::create_navigation_mesh() {
 	return nm;
 }
 
-extern bool (*array_mesh_lightmap_unwrap_callback)(float p_texel_size, const float *p_vertices, const float *p_normals, int p_vertex_count, const int *p_indices, int p_index_count, float **r_uv, int **r_vertex, int *r_vertex_count, int **r_index, int *r_index_count, int *r_size_hint_x, int *r_size_hint_y, int *&r_cache_data, unsigned int &r_cache_size, bool &r_used_cache);
+extern bool (*array_mesh_lightmap_unwrap_callback)(float p_texel_size, const float *p_vertices, const float *p_normals, int p_vertex_count, const int *p_indices, int p_index_count, const uint8_t *p_cache_data, bool *r_use_cache, uint8_t **r_mesh_cache, int *r_mesh_cache_size, float **r_uv, int **r_vertex, int *r_vertex_count, int **r_index, int *r_index_count, int *r_size_hint_x, int *r_size_hint_y);
 
 struct EditorSceneImporterMeshLightmapSurface {
 	Ref<Material> material;
@@ -593,22 +593,24 @@ struct EditorSceneImporterMeshLightmapSurface {
 	String name;
 };
 
-Error EditorSceneImporterMesh::lightmap_unwrap_cached(int *&r_cache_data, unsigned int &r_cache_size, bool &r_used_cache, const Transform &p_base_transform, float p_texel_size) {
+Error EditorSceneImporterMesh::lightmap_unwrap_cached(const Transform &p_base_transform, float p_texel_size, const Vector<uint8_t> &p_src_cache, Vector<uint8_t> &r_dst_cache) {
 	ERR_FAIL_COND_V(!array_mesh_lightmap_unwrap_callback, ERR_UNCONFIGURED);
 	ERR_FAIL_COND_V_MSG(blend_shapes.size() != 0, ERR_UNAVAILABLE, "Can't unwrap mesh with blend shapes.");
 
-	Vector<float> vertices;
-	Vector<float> normals;
-	Vector<int> indices;
-	Vector<float> uv;
-	Vector<Pair<int, int>> uv_indices;
+	LocalVector<float> vertices;
+	LocalVector<float> normals;
+	LocalVector<int> indices;
+	LocalVector<float> uv;
+	LocalVector<Pair<int, int>> uv_indices;
 
 	Vector<EditorSceneImporterMeshLightmapSurface> lightmap_surfaces;
 
 	// Keep only the scale
-	Transform transform = p_base_transform;
-	transform.origin = Vector3();
-	transform.looking_at(Vector3(1, 0, 0), Vector3(0, 1, 0));
+	Basis basis = p_base_transform.get_basis();
+	Vector3 scale = Vector3(basis.get_axis(0).length(), basis.get_axis(1).length(), basis.get_axis(2).length());
+
+	Transform transform;
+	transform.scale(scale);
 
 	Basis normal_basis = transform.basis.inverse().transposed();
 
@@ -623,15 +625,10 @@ Error EditorSceneImporterMesh::lightmap_unwrap_cached(int *&r_cache_data, unsign
 
 		SurfaceTool::create_vertex_array_from_triangle_arrays(arrays, s.vertices, &s.format);
 
-		Vector<Vector3> rvertices = arrays[Mesh::ARRAY_VERTEX];
+		PackedVector3Array rvertices = arrays[Mesh::ARRAY_VERTEX];
 		int vc = rvertices.size();
-		const Vector3 *r = rvertices.ptr();
-
-		Vector<Vector3> rnormals = arrays[Mesh::ARRAY_NORMAL];
-
-		ERR_FAIL_COND_V_MSG(rnormals.size() == 0, ERR_UNAVAILABLE, "Normals are required for lightmap unwrap.");
 
-		const Vector3 *rn = rnormals.ptr();
+		PackedVector3Array rnormals = arrays[Mesh::ARRAY_NORMAL];
 
 		int vertex_ofs = vertices.size() / 3;
 
@@ -640,24 +637,29 @@ Error EditorSceneImporterMesh::lightmap_unwrap_cached(int *&r_cache_data, unsign
 		uv_indices.resize(vertex_ofs + vc);
 
 		for (int j = 0; j < vc; j++) {
-			Vector3 v = transform.xform(r[j]);
-			Vector3 n = normal_basis.xform(rn[j]).normalized();
-
-			vertices.write[(j + vertex_ofs) * 3 + 0] = v.x;
-			vertices.write[(j + vertex_ofs) * 3 + 1] = v.y;
-			vertices.write[(j + vertex_ofs) * 3 + 2] = v.z;
-			normals.write[(j + vertex_ofs) * 3 + 0] = n.x;
-			normals.write[(j + vertex_ofs) * 3 + 1] = n.y;
-			normals.write[(j + vertex_ofs) * 3 + 2] = n.z;
-			uv_indices.write[j + vertex_ofs] = Pair<int, int>(i, j);
+			Vector3 v = transform.xform(rvertices[j]);
+			Vector3 n = normal_basis.xform(rnormals[j]).normalized();
+
+			vertices[(j + vertex_ofs) * 3 + 0] = v.x;
+			vertices[(j + vertex_ofs) * 3 + 1] = v.y;
+			vertices[(j + vertex_ofs) * 3 + 2] = v.z;
+			normals[(j + vertex_ofs) * 3 + 0] = n.x;
+			normals[(j + vertex_ofs) * 3 + 1] = n.y;
+			normals[(j + vertex_ofs) * 3 + 2] = n.z;
+			uv_indices[j + vertex_ofs] = Pair<int, int>(i, j);
 		}
 
-		Vector<int> rindices = arrays[Mesh::ARRAY_INDEX];
+		PackedInt32Array rindices = arrays[Mesh::ARRAY_INDEX];
 		int ic = rindices.size();
 
+		float eps = 1.19209290e-7F; // Taken from xatlas.h
 		if (ic == 0) {
 			for (int j = 0; j < vc / 3; j++) {
-				if (Face3(r[j * 3 + 0], r[j * 3 + 1], r[j * 3 + 2]).is_degenerate()) {
+				Vector3 p0 = transform.xform(rvertices[j * 3 + 0]);
+				Vector3 p1 = transform.xform(rvertices[j * 3 + 1]);
+				Vector3 p2 = transform.xform(rvertices[j * 3 + 2]);
+
+				if ((p0 - p1).length_squared() < eps || (p1 - p2).length_squared() < eps || (p2 - p0).length_squared() < eps) {
 					continue;
 				}
 
@@ -667,15 +669,18 @@ Error EditorSceneImporterMesh::lightmap_unwrap_cached(int *&r_cache_data, unsign
 			}
 
 		} else {
-			const int *ri = rindices.ptr();
-
 			for (int j = 0; j < ic / 3; j++) {
-				if (Face3(r[ri[j * 3 + 0]], r[ri[j * 3 + 1]], r[ri[j * 3 + 2]]).is_degenerate()) {
+				Vector3 p0 = transform.xform(rvertices[rindices[j * 3 + 0]]);
+				Vector3 p1 = transform.xform(rvertices[rindices[j * 3 + 1]]);
+				Vector3 p2 = transform.xform(rvertices[rindices[j * 3 + 2]]);
+
+				if ((p0 - p1).length_squared() < eps || (p1 - p2).length_squared() < eps || (p2 - p0).length_squared() < eps) {
 					continue;
 				}
-				indices.push_back(vertex_ofs + ri[j * 3 + 0]);
-				indices.push_back(vertex_ofs + ri[j * 3 + 1]);
-				indices.push_back(vertex_ofs + ri[j * 3 + 2]);
+
+				indices.push_back(vertex_ofs + rindices[j * 3 + 0]);
+				indices.push_back(vertex_ofs + rindices[j * 3 + 1]);
+				indices.push_back(vertex_ofs + rindices[j * 3 + 2]);
 			}
 		}
 
@@ -684,6 +689,9 @@ Error EditorSceneImporterMesh::lightmap_unwrap_cached(int *&r_cache_data, unsign
 
 	//unwrap
 
+	bool use_cache = true; // Used to request cache generation and to know if cache was used
+	uint8_t *gen_cache;
+	int gen_cache_size;
 	float *gen_uvs;
 	int *gen_vertices;
 	int *gen_indices;
@@ -692,7 +700,7 @@ Error EditorSceneImporterMesh::lightmap_unwrap_cached(int *&r_cache_data, unsign
 	int size_x;
 	int size_y;
 
-	bool ok = array_mesh_lightmap_unwrap_callback(p_texel_size, vertices.ptr(), normals.ptr(), vertices.size() / 3, indices.ptr(), indices.size(), &gen_uvs, &gen_vertices, &gen_vertex_count, &gen_indices, &gen_index_count, &size_x, &size_y, r_cache_data, r_cache_size, r_used_cache);
+	bool ok = array_mesh_lightmap_unwrap_callback(p_texel_size, vertices.ptr(), normals.ptr(), vertices.size() / 3, indices.ptr(), indices.size(), p_src_cache.ptr(), &use_cache, &gen_cache, &gen_cache_size, &gen_uvs, &gen_vertices, &gen_vertex_count, &gen_indices, &gen_index_count, &size_x, &size_y);
 
 	if (!ok) {
 		return ERR_CANT_CREATE;
@@ -702,7 +710,7 @@ Error EditorSceneImporterMesh::lightmap_unwrap_cached(int *&r_cache_data, unsign
 	clear();
 
 	//create surfacetools for each surface..
-	Vector<Ref<SurfaceTool>> surfaces_tools;
+	LocalVector<Ref<SurfaceTool>> surfaces_tools;
 
 	for (int i = 0; i < lightmap_surfaces.size(); i++) {
 		Ref<SurfaceTool> st;
@@ -714,11 +722,12 @@ Error EditorSceneImporterMesh::lightmap_unwrap_cached(int *&r_cache_data, unsign
 	}
 
 	print_verbose("Mesh: Gen indices: " + itos(gen_index_count));
+
 	//go through all indices
 	for (int i = 0; i < gen_index_count; i += 3) {
-		ERR_FAIL_INDEX_V(gen_vertices[gen_indices[i + 0]], uv_indices.size(), ERR_BUG);
-		ERR_FAIL_INDEX_V(gen_vertices[gen_indices[i + 1]], uv_indices.size(), ERR_BUG);
-		ERR_FAIL_INDEX_V(gen_vertices[gen_indices[i + 2]], uv_indices.size(), ERR_BUG);
+		ERR_FAIL_INDEX_V(gen_vertices[gen_indices[i + 0]], (int)uv_indices.size(), ERR_BUG);
+		ERR_FAIL_INDEX_V(gen_vertices[gen_indices[i + 1]], (int)uv_indices.size(), ERR_BUG);
+		ERR_FAIL_INDEX_V(gen_vertices[gen_indices[i + 2]], (int)uv_indices.size(), ERR_BUG);
 
 		ERR_FAIL_COND_V(uv_indices[gen_vertices[gen_indices[i + 0]]].first != uv_indices[gen_vertices[gen_indices[i + 1]]].first || uv_indices[gen_vertices[gen_indices[i + 0]]].first != uv_indices[gen_vertices[gen_indices[i + 2]]].first, ERR_BUG);
 
@@ -728,49 +737,54 @@ Error EditorSceneImporterMesh::lightmap_unwrap_cached(int *&r_cache_data, unsign
 			SurfaceTool::Vertex v = lightmap_surfaces[surface].vertices[uv_indices[gen_vertices[gen_indices[i + j]]].second];
 
 			if (lightmap_surfaces[surface].format & Mesh::ARRAY_FORMAT_COLOR) {
-				surfaces_tools.write[surface]->set_color(v.color);
+				surfaces_tools[surface]->set_color(v.color);
 			}
 			if (lightmap_surfaces[surface].format & Mesh::ARRAY_FORMAT_TEX_UV) {
-				surfaces_tools.write[surface]->set_uv(v.uv);
+				surfaces_tools[surface]->set_uv(v.uv);
 			}
 			if (lightmap_surfaces[surface].format & Mesh::ARRAY_FORMAT_NORMAL) {
-				surfaces_tools.write[surface]->set_normal(v.normal);
+				surfaces_tools[surface]->set_normal(v.normal);
 			}
 			if (lightmap_surfaces[surface].format & Mesh::ARRAY_FORMAT_TANGENT) {
 				Plane t;
 				t.normal = v.tangent;
 				t.d = v.binormal.dot(v.normal.cross(v.tangent)) < 0 ? -1 : 1;
-				surfaces_tools.write[surface]->set_tangent(t);
+				surfaces_tools[surface]->set_tangent(t);
 			}
 			if (lightmap_surfaces[surface].format & Mesh::ARRAY_FORMAT_BONES) {
-				surfaces_tools.write[surface]->set_bones(v.bones);
+				surfaces_tools[surface]->set_bones(v.bones);
 			}
 			if (lightmap_surfaces[surface].format & Mesh::ARRAY_FORMAT_WEIGHTS) {
-				surfaces_tools.write[surface]->set_weights(v.weights);
+				surfaces_tools[surface]->set_weights(v.weights);
 			}
 
 			Vector2 uv2(gen_uvs[gen_indices[i + j] * 2 + 0], gen_uvs[gen_indices[i + j] * 2 + 1]);
-			surfaces_tools.write[surface]->set_uv2(uv2);
+			surfaces_tools[surface]->set_uv2(uv2);
 
-			surfaces_tools.write[surface]->add_vertex(v.vertex);
+			surfaces_tools[surface]->add_vertex(v.vertex);
 		}
 	}
 
 	//generate surfaces
-
-	for (int i = 0; i < surfaces_tools.size(); i++) {
-		surfaces_tools.write[i]->index();
-		Array arrays = surfaces_tools.write[i]->commit_to_arrays();
-		add_surface(surfaces_tools.write[i]->get_primitive(), arrays, Array(), Dictionary(), surfaces_tools.write[i]->get_material(), surfaces_tools.write[i]->get_meta("name"));
+	for (unsigned int i = 0; i < surfaces_tools.size(); i++) {
+		surfaces_tools[i]->index();
+		Array arrays = surfaces_tools[i]->commit_to_arrays();
+		add_surface(surfaces_tools[i]->get_primitive(), arrays, Array(), Dictionary(), surfaces_tools[i]->get_material(), surfaces_tools[i]->get_meta("name"));
 	}
 
 	set_lightmap_size_hint(Size2(size_x, size_y));
 
-	if (!r_used_cache) {
-		//free stuff
-		::free(gen_vertices);
-		::free(gen_indices);
-		::free(gen_uvs);
+	if (gen_cache_size > 0) {
+		r_dst_cache.resize(gen_cache_size);
+		memcpy(r_dst_cache.ptrw(), gen_cache, gen_cache_size);
+		memfree(gen_cache);
+	}
+
+	if (!use_cache) {
+		// Cache was not used, free the buffers
+		memfree(gen_vertices);
+		memfree(gen_indices);
+		memfree(gen_uvs);
 	}
 
 	return OK;

+ 1 - 1
editor/import/scene_importer_mesh.h

@@ -105,7 +105,7 @@ public:
 	Vector<Ref<Shape3D>> convex_decompose() const;
 	Ref<Shape3D> create_trimesh_shape() const;
 	Ref<NavigationMesh> create_navigation_mesh();
-	Error lightmap_unwrap_cached(int *&r_cache_data, unsigned int &r_cache_size, bool &r_used_cache, const Transform &p_base_transform, float p_texel_size);
+	Error lightmap_unwrap_cached(const Transform &p_base_transform, float p_texel_size, const Vector<uint8_t> &p_src_cache, Vector<uint8_t> &r_dst_cache);
 
 	void set_lightmap_size_hint(const Size2i &p_size);
 	Size2i get_lightmap_size_hint() const;

+ 37 - 49
modules/lightmapper_rd/lightmapper_rd.cpp

@@ -162,8 +162,8 @@ Lightmapper::BakeError LightmapperRD::_blit_meshes_into_atlas(int p_max_texture_
 		MeshInstance &mi = mesh_instances.write[m_i];
 		Size2i s = Size2i(mi.data.albedo_on_uv2->get_width(), mi.data.albedo_on_uv2->get_height());
 		sizes.push_back(s);
-		atlas_size.width = MAX(atlas_size.width, s.width);
-		atlas_size.height = MAX(atlas_size.height, s.height);
+		atlas_size.width = MAX(atlas_size.width, s.width + 2);
+		atlas_size.height = MAX(atlas_size.height, s.height + 2);
 	}
 
 	int max = nearest_power_of_2_templated(atlas_size.width);
@@ -186,10 +186,12 @@ Lightmapper::BakeError LightmapperRD::_blit_meshes_into_atlas(int p_max_texture_
 
 	//determine best texture array atlas size by bruteforce fitting
 	while (atlas_size.x <= p_max_texture_size && atlas_size.y <= p_max_texture_size) {
-		Vector<Vector2i> source_sizes = sizes;
+		Vector<Vector2i> source_sizes;
 		Vector<int> source_indices;
-		source_indices.resize(source_sizes.size());
+		source_sizes.resize(sizes.size());
+		source_indices.resize(sizes.size());
 		for (int i = 0; i < source_indices.size(); i++) {
+			source_sizes.write[i] = sizes[i] + Vector2i(2, 2); // Add padding between lightmaps
 			source_indices.write[i] = i;
 		}
 		Vector<Vector3i> atlas_offsets;
@@ -207,7 +209,7 @@ Lightmapper::BakeError LightmapperRD::_blit_meshes_into_atlas(int p_max_texture_
 				if (ofs.z > 0) {
 					//valid
 					ofs.z = slices;
-					atlas_offsets.write[sidx] = ofs;
+					atlas_offsets.write[sidx] = ofs + Vector3i(1, 1, 0); // Center lightmap in the reserved oversized region
 				} else {
 					new_indices.push_back(sidx);
 					new_sources.push_back(source_sizes[i]);
@@ -272,7 +274,7 @@ Lightmapper::BakeError LightmapperRD::_blit_meshes_into_atlas(int p_max_texture_
 	return BAKE_OK;
 }
 
-void LightmapperRD::_create_acceleration_structures(RenderingDevice *rd, Size2i atlas_size, int atlas_slices, AABB &bounds, int grid_size, Vector<Probe> &probe_positions, GenerateProbes p_generate_probes, Vector<int> &slice_triangle_count, Vector<int> &slice_seam_count, RID &vertex_buffer, RID &triangle_buffer, RID &box_buffer, RID &lights_buffer, RID &triangle_cell_indices_buffer, RID &probe_positions_buffer, RID &grid_texture, RID &grid_texture_sdf, RID &seams_buffer, BakeStepFunc p_step_function, void *p_bake_userdata) {
+void LightmapperRD::_create_acceleration_structures(RenderingDevice *rd, Size2i atlas_size, int atlas_slices, AABB &bounds, int grid_size, Vector<Probe> &probe_positions, GenerateProbes p_generate_probes, Vector<int> &slice_triangle_count, Vector<int> &slice_seam_count, RID &vertex_buffer, RID &triangle_buffer, RID &box_buffer, RID &lights_buffer, RID &triangle_cell_indices_buffer, RID &probe_positions_buffer, RID &grid_texture, RID &seams_buffer, BakeStepFunc p_step_function, void *p_bake_userdata) {
 	HashMap<Vertex, uint32_t, VertexHash> vertex_map;
 
 	//fill triangles array and vertex array
@@ -482,14 +484,6 @@ void LightmapperRD::_create_acceleration_structures(RenderingDevice *rd, Size2i
 		img->save_png("res://grid_layer_" + itos(1000 + i).substr(1, 3) + ".png");
 	}
 #endif
-	if (p_step_function) {
-		p_step_function(0.45, TTR("Generating Signed Distance Field"), p_bake_userdata, true);
-	}
-
-	//generate SDF for raytracing
-	Vector<uint32_t> euclidean_pos = Geometry3D::generate_edf(solid, Vector3i(grid_size, grid_size, grid_size), false);
-	Vector<uint32_t> euclidean_neg = Geometry3D::generate_edf(solid, Vector3i(grid_size, grid_size, grid_size), true);
-	Vector<int8_t> sdf8 = Geometry3D::generate_sdf8(euclidean_pos, euclidean_neg);
 
 	/*****************************/
 	/*** CREATE GPU STRUCTURES ***/
@@ -551,10 +545,6 @@ void LightmapperRD::_create_acceleration_structures(RenderingDevice *rd, Size2i
 		tf.format = RD::DATA_FORMAT_R32G32_UINT;
 		texdata.write[0] = grid_indices.to_byte_array();
 		grid_texture = rd->texture_create(tf, RD::TextureView(), texdata);
-		//sdf
-		tf.format = RD::DATA_FORMAT_R8_SNORM;
-		texdata.write[0] = sdf8.to_byte_array();
-		grid_texture_sdf = rd->texture_create(tf, RD::TextureView(), texdata);
 	}
 }
 
@@ -755,8 +745,7 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
 			light_environment_tex = rd->texture_create(tfp, RD::TextureView(), tdata);
 
 #ifdef DEBUG_TEXTURES
-			panorama_tex->convert(Image::FORMAT_RGB8);
-			panorama_tex->save_png("res://0_panorama.png");
+			panorama_tex->save_exr("res://0_panorama.exr", false);
 #endif
 		}
 	}
@@ -770,7 +759,6 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
 	RID lights_buffer;
 	RID triangle_cell_indices_buffer;
 	RID grid_texture;
-	RID grid_texture_sdf;
 	RID seams_buffer;
 	RID probe_positions_buffer;
 
@@ -783,11 +771,10 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
 	rd->free(lights_buffer);                \
 	rd->free(triangle_cell_indices_buffer); \
 	rd->free(grid_texture);                 \
-	rd->free(grid_texture_sdf);             \
 	rd->free(seams_buffer);                 \
 	rd->free(probe_positions_buffer);
 
-	_create_acceleration_structures(rd, atlas_size, atlas_slices, bounds, grid_size, probe_positions, p_generate_probes, slice_triangle_count, slice_seam_count, vertex_buffer, triangle_buffer, box_buffer, lights_buffer, triangle_cell_indices_buffer, probe_positions_buffer, grid_texture, grid_texture_sdf, seams_buffer, p_step_function, p_bake_userdata);
+	_create_acceleration_structures(rd, atlas_size, atlas_slices, bounds, grid_size, probe_positions, p_generate_probes, slice_triangle_count, slice_seam_count, vertex_buffer, triangle_buffer, box_buffer, lights_buffer, triangle_cell_indices_buffer, probe_positions_buffer, grid_texture, seams_buffer, p_step_function, p_bake_userdata);
 
 	if (p_step_function) {
 		p_step_function(0.47, TTR("Preparing shaders"), p_bake_userdata, true);
@@ -883,27 +870,20 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
 			RD::Uniform u;
 			u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
 			u.binding = 9;
-			u.ids.push_back(grid_texture_sdf);
-			base_uniforms.push_back(u);
-		}
-		{
-			RD::Uniform u;
-			u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
-			u.binding = 10;
 			u.ids.push_back(albedo_array_tex);
 			base_uniforms.push_back(u);
 		}
 		{
 			RD::Uniform u;
 			u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
-			u.binding = 11;
+			u.binding = 10;
 			u.ids.push_back(emission_array_tex);
 			base_uniforms.push_back(u);
 		}
 		{
 			RD::Uniform u;
 			u.uniform_type = RD::UNIFORM_TYPE_SAMPLER;
-			u.binding = 12;
+			u.binding = 11;
 			u.ids.push_back(sampler);
 			base_uniforms.push_back(u);
 		}
@@ -937,13 +917,11 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
 		Ref<Image> img;
 		img.instance();
 		img->create(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBAF, s);
-		img->convert(Image::FORMAT_RGBA8);
-		img->save_png("res://1_position_" + itos(i) + ".png");
+		img->save_exr("res://1_position_" + itos(i) + ".exr", false);
 
 		s = rd->texture_get_data(normal_tex, i);
 		img->create(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBAH, s);
-		img->convert(Image::FORMAT_RGBA8);
-		img->save_png("res://1_normal_" + itos(i) + ".png");
+		img->save_exr("res://1_normal_" + itos(i) + ".exr", false);
 	}
 #endif
 
@@ -966,27 +944,27 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
 	}
 	ERR_FAIL_COND_V(err != OK, BAKE_ERROR_LIGHTMAP_CANT_PRE_BAKE_MESHES);
 
-	//unoccluder
+	// Unoccluder
 	RID compute_shader_unocclude = rd->shader_create_from_bytecode(compute_shader->get_bytecode("unocclude"));
 	ERR_FAIL_COND_V(compute_shader_unocclude.is_null(), BAKE_ERROR_LIGHTMAP_CANT_PRE_BAKE_MESHES); // internal check, should not happen
 	RID compute_shader_unocclude_pipeline = rd->compute_pipeline_create(compute_shader_unocclude);
 
-	//direct light
+	// Direct light
 	RID compute_shader_primary = rd->shader_create_from_bytecode(compute_shader->get_bytecode("primary"));
 	ERR_FAIL_COND_V(compute_shader_primary.is_null(), BAKE_ERROR_LIGHTMAP_CANT_PRE_BAKE_MESHES); // internal check, should not happen
 	RID compute_shader_primary_pipeline = rd->compute_pipeline_create(compute_shader_primary);
 
-	//indirect light
+	// Indirect light
 	RID compute_shader_secondary = rd->shader_create_from_bytecode(compute_shader->get_bytecode("secondary"));
 	ERR_FAIL_COND_V(compute_shader_secondary.is_null(), BAKE_ERROR_LIGHTMAP_CANT_PRE_BAKE_MESHES); //internal check, should not happen
 	RID compute_shader_secondary_pipeline = rd->compute_pipeline_create(compute_shader_secondary);
 
-	//dilate
+	// Dilate
 	RID compute_shader_dilate = rd->shader_create_from_bytecode(compute_shader->get_bytecode("dilate"));
 	ERR_FAIL_COND_V(compute_shader_dilate.is_null(), BAKE_ERROR_LIGHTMAP_CANT_PRE_BAKE_MESHES); //internal check, should not happen
 	RID compute_shader_dilate_pipeline = rd->compute_pipeline_create(compute_shader_dilate);
 
-	//dilate
+	// Light probes
 	RID compute_shader_light_probes = rd->shader_create_from_bytecode(compute_shader->get_bytecode("light_probes"));
 	ERR_FAIL_COND_V(compute_shader_light_probes.is_null(), BAKE_ERROR_LIGHTMAP_CANT_PRE_BAKE_MESHES); //internal check, should not happen
 	RID compute_shader_light_probes_pipeline = rd->compute_pipeline_create(compute_shader_light_probes);
@@ -1153,8 +1131,7 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
 		Ref<Image> img;
 		img.instance();
 		img->create(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBAH, s);
-		img->convert(Image::FORMAT_RGBA8);
-		img->save_png("res://2_light_primary_" + itos(i) + ".png");
+		img->save_exr("res://2_light_primary_" + itos(i) + ".exr", false);
 	}
 #endif
 
@@ -1212,7 +1189,7 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
 				RD::Uniform u;
 				u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
 				u.binding = 6;
-				u.ids.push_back(light_environment_tex); //reuse unocclude tex
+				u.ids.push_back(light_environment_tex);
 				uniforms.push_back(u);
 			}
 		}
@@ -1298,7 +1275,15 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
 					}
 				}
 			}
+
+			if (b == 0) {
+				// This disables the environment for subsequent bounces
+				push_constant.environment_xform[3] = -99.0f;
+			}
 		}
+
+		// Restore the correct environment transform
+		push_constant.environment_xform[3] = 0.0f;
 	}
 
 	/* LIGHPROBES */
@@ -1449,8 +1434,7 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
 		Ref<Image> img;
 		img.instance();
 		img->create(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBAH, s);
-		img->convert(Image::FORMAT_RGBA8);
-		img->save_png("res://4_light_secondary_" + itos(i) + ".png");
+		img->save_exr("res://4_light_secondary_" + itos(i) + ".exr", false);
 	}
 #endif
 
@@ -1582,6 +1566,11 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
 		clear_colors.push_back(Color(0, 0, 0, 1));
 		for (int i = 0; i < atlas_slices; i++) {
 			int subslices = (p_bake_sh ? 4 : 1);
+
+			if (slice_seam_count[i] == 0) {
+				continue;
+			}
+
 			for (int k = 0; k < subslices; k++) {
 				RasterSeamsPushConstant seams_push_constant;
 				seams_push_constant.slice = uint32_t(i * subslices + k);
@@ -1654,8 +1643,7 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
 		Ref<Image> img;
 		img.instance();
 		img->create(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBAH, s);
-		img->convert(Image::FORMAT_RGBA8);
-		img->save_png("res://5_blendseams" + itos(i) + ".png");
+		img->save_exr("res://5_blendseams" + itos(i) + ".exr", false);
 	}
 #endif
 	if (p_step_function) {
@@ -1682,7 +1670,7 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
 			Ref<Image> img2;
 			img2.instance();
 			img2->create(probe_values.size(), 1, false, Image::FORMAT_RGBAF, probe_data);
-			img2->save_png("res://6_lightprobes.png");
+			img2->save_exr("res://6_lightprobes.exr", false);
 		}
 #endif
 	}

+ 1 - 1
modules/lightmapper_rd/lightmapper_rd.h

@@ -231,7 +231,7 @@ class LightmapperRD : public Lightmapper {
 	Vector<Color> probe_values;
 
 	BakeError _blit_meshes_into_atlas(int p_max_texture_size, Vector<Ref<Image>> &albedo_images, Vector<Ref<Image>> &emission_images, AABB &bounds, Size2i &atlas_size, int &atlas_slices, BakeStepFunc p_step_function, void *p_bake_userdata);
-	void _create_acceleration_structures(RenderingDevice *rd, Size2i atlas_size, int atlas_slices, AABB &bounds, int grid_size, Vector<Probe> &probe_positions, GenerateProbes p_generate_probes, Vector<int> &slice_triangle_count, Vector<int> &slice_seam_count, RID &vertex_buffer, RID &triangle_buffer, RID &box_buffer, RID &lights_buffer, RID &triangle_cell_indices_buffer, RID &probe_positions_buffer, RID &grid_texture, RID &grid_texture_sdf, RID &seams_buffer, BakeStepFunc p_step_function, void *p_bake_userdata);
+	void _create_acceleration_structures(RenderingDevice *rd, Size2i atlas_size, int atlas_slices, AABB &bounds, int grid_size, Vector<Probe> &probe_positions, GenerateProbes p_generate_probes, Vector<int> &slice_triangle_count, Vector<int> &slice_seam_count, RID &vertex_buffer, RID &triangle_buffer, RID &box_buffer, RID &lights_buffer, RID &triangle_cell_indices_buffer, RID &probe_positions_buffer, RID &grid_texture, RID &seams_buffer, BakeStepFunc p_step_function, void *p_bake_userdata);
 	void _raster_geometry(RenderingDevice *rd, Size2i atlas_size, int atlas_slices, int grid_size, AABB bounds, float p_bias, Vector<int> slice_triangle_count, RID position_tex, RID unocclude_tex, RID normal_tex, RID raster_depth_buffer, RID rasterize_shader, RID raster_base_uniform);
 
 public:

+ 3 - 4
modules/lightmapper_rd/lm_common_inc.glsl

@@ -84,9 +84,8 @@ layout(set = 0, binding = 7, std430) restrict readonly buffer Probes {
 probe_positions;
 
 layout(set = 0, binding = 8) uniform utexture3D grid;
-layout(set = 0, binding = 9) uniform texture3D grid_sdf;
 
-layout(set = 0, binding = 10) uniform texture2DArray albedo_tex;
-layout(set = 0, binding = 11) uniform texture2DArray emission_tex;
+layout(set = 0, binding = 9) uniform texture2DArray albedo_tex;
+layout(set = 0, binding = 10) uniform texture2DArray emission_tex;
 
-layout(set = 0, binding = 12) uniform sampler linear_sampler;
+layout(set = 0, binding = 11) uniform sampler linear_sampler;

+ 13 - 8
modules/lightmapper_rd/lm_compute.glsl

@@ -96,15 +96,22 @@ params;
 bool ray_hits_triangle(vec3 from, vec3 dir, float max_dist, vec3 p0, vec3 p1, vec3 p2, out float r_distance, out vec3 r_barycentric) {
 	const vec3 e0 = p1 - p0;
 	const vec3 e1 = p0 - p2;
-	vec3 triangleNormal = cross(e1, e0);
+	vec3 triangle_normal = cross(e1, e0);
 
-	const vec3 e2 = (1.0 / dot(triangleNormal, dir)) * (p0 - from);
+	float n_dot_dir = dot(triangle_normal, dir);
+
+	if (abs(n_dot_dir) < 0.01) {
+		return false;
+	}
+
+	const vec3 e2 = (p0 - from) / n_dot_dir;
 	const vec3 i = cross(dir, e2);
 
 	r_barycentric.y = dot(i, e1);
 	r_barycentric.z = dot(i, e0);
 	r_barycentric.x = 1.0 - (r_barycentric.z + r_barycentric.y);
-	r_distance = dot(triangleNormal, e2);
+	r_distance = dot(triangle_normal, e2);
+
 	return (r_distance > params.bias) && (r_distance < max_dist) && all(greaterThanEqual(r_barycentric, vec3(0.0)));
 }
 
@@ -307,8 +314,6 @@ void main() {
 				continue;
 			}
 
-			d /= lights.data[i].range;
-
 			attenuation = get_omni_attenuation(d, 1.0 / lights.data[i].range, lights.data[i].attenuation);
 
 			if (lights.data[i].type == LIGHT_TYPE_SPOT) {
@@ -410,7 +415,7 @@ void main() {
 		uint tidx;
 		vec3 barycentric;
 
-		vec3 light;
+		vec3 light = vec3(0.0);
 		if (trace_ray(position + ray_dir * params.bias, position + ray_dir * length(params.world_size), tidx, barycentric)) {
 			//hit a triangle
 			vec2 uv0 = vertices.data[triangles.data[tidx].indices.x].uv;
@@ -419,8 +424,8 @@ void main() {
 			vec3 uvw = vec3(barycentric.x * uv0 + barycentric.y * uv1 + barycentric.z * uv2, float(triangles.data[tidx].slice));
 
 			light = textureLod(sampler2DArray(source_light, linear_sampler), uvw, 0.0).rgb;
-		} else {
-			//did not hit a triangle, reach out for the sky
+		} else if (params.env_transform[0][3] == 0.0) { // Use env_transform[0][3] to indicate when we are computing the first bounce
+			// Did not hit a triangle, reach out for the sky
 			vec3 sky_dir = normalize(mat3(params.env_transform) * ray_dir);
 
 			vec2 st = vec2(

+ 1 - 1
modules/tinyexr/image_saver_tinyexr.cpp

@@ -169,7 +169,7 @@ Error save_exr(const String &p_path, const Ref<Image> &p_img, bool p_grayscale)
 		{ 0 }, // R
 		{ 1, 0 }, // GR
 		{ 2, 1, 0 }, // BGR
-		{ 2, 1, 0, 3 } // BGRA
+		{ 3, 2, 1, 0 } // ABGR
 	};
 
 	int channel_count = get_channel_count(format);

+ 99 - 111
modules/xatlas_unwrap/register_types.cpp

@@ -29,26 +29,19 @@
 /*************************************************************************/
 
 #include "register_types.h"
-
-#include "core/error/error_macros.h"
-
 #include "core/crypto/crypto_core.h"
-
 #include "thirdparty/xatlas/xatlas.h"
 
-#include <stdio.h>
-#include <stdlib.h>
+extern bool (*array_mesh_lightmap_unwrap_callback)(float p_texel_size, const float *p_vertices, const float *p_normals, int p_vertex_count, const int *p_indices, int p_index_count, const uint8_t *p_cache_data, bool *r_use_cache, uint8_t **r_mesh_cache, int *r_mesh_cache_size, float **r_uv, int **r_vertex, int *r_vertex_count, int **r_index, int *r_index_count, int *r_size_hint_x, int *r_size_hint_y);
 
-extern bool (*array_mesh_lightmap_unwrap_callback)(float p_texel_size, const float *p_vertices, const float *p_normals, int p_vertex_count, const int *p_indices, int p_index_count, float **r_uv, int **r_vertex, int *r_vertex_count, int **r_index, int *r_index_count, int *r_size_hint_x, int *r_size_hint_y, int *&r_cache_data, unsigned int &r_cache_size, bool &r_used_cache);
-
-bool xatlas_mesh_lightmap_unwrap_callback(float p_texel_size, const float *p_vertices, const float *p_normals, int p_vertex_count, const int *p_indices, int p_index_count, float **r_uvs, int **r_vertices, int *r_vertex_count, int **r_indices, int *r_index_count, int *r_size_hint_x, int *r_size_hint_y, int *&r_cache_data, unsigned int &r_cache_size, bool &r_used_cache) {
+bool xatlas_mesh_lightmap_unwrap_callback(float p_texel_size, const float *p_vertices, const float *p_normals, int p_vertex_count, const int *p_indices, int p_index_count, const uint8_t *p_cache_data, bool *r_use_cache, uint8_t **r_mesh_cache, int *r_mesh_cache_size, float **r_uv, int **r_vertex, int *r_vertex_count, int **r_index, int *r_index_count, int *r_size_hint_x, int *r_size_hint_y) {
 	CryptoCore::MD5Context ctx;
 	ctx.start();
 
 	ctx.update((unsigned char *)&p_texel_size, sizeof(float));
 	ctx.update((unsigned char *)p_indices, sizeof(int) * p_index_count);
-	ctx.update((unsigned char *)p_vertices, sizeof(float) * p_vertex_count);
-	ctx.update((unsigned char *)p_normals, sizeof(float) * p_vertex_count);
+	ctx.update((unsigned char *)p_vertices, sizeof(float) * p_vertex_count * 3);
+	ctx.update((unsigned char *)p_normals, sizeof(float) * p_vertex_count * 3);
 
 	unsigned char hash[16];
 	ctx.finish(hash);
@@ -56,38 +49,37 @@ bool xatlas_mesh_lightmap_unwrap_callback(float p_texel_size, const float *p_ver
 	bool cached = false;
 	unsigned int cache_idx = 0;
 
-	if (r_used_cache && r_cache_size) {
-		//Check if hash is in cache data
+	*r_mesh_cache = nullptr;
+	*r_mesh_cache_size = 0;
 
-		int *cache_data = r_cache_data;
+	if (p_cache_data) {
+		//Check if hash is in cache data
+		int *cache_data = (int *)p_cache_data;
 		int n_entries = cache_data[0];
-		unsigned int r_idx = 1;
+		unsigned int read_idx = 1;
 		for (int i = 0; i < n_entries; ++i) {
-			if (memcmp(&cache_data[r_idx], hash, 16) == 0) {
+			if (memcmp(&cache_data[read_idx], hash, 16) == 0) {
 				cached = true;
-				cache_idx = r_idx;
+				cache_idx = read_idx;
 				break;
 			}
 
-			r_idx += 4; // hash
-			r_idx += 2; // size hint
+			read_idx += 4; // hash
+			read_idx += 2; // size hint
 
-			int vertex_count = cache_data[r_idx];
-			r_idx += 1; // vertex count
-			r_idx += vertex_count; // vertex
-			r_idx += vertex_count * 2; // uvs
+			int vertex_count = cache_data[read_idx];
+			read_idx += 1; // vertex count
+			read_idx += vertex_count; // vertex
+			read_idx += vertex_count * 2; // uvs
 
-			int index_count = cache_data[r_idx];
-			r_idx += 1; // index count
-			r_idx += index_count; // indices
+			int index_count = cache_data[read_idx];
+			read_idx += 1; // index count
+			read_idx += index_count; // indices
 		}
 	}
 
-	if (r_used_cache && cached) {
-		int *cache_data = r_cache_data;
-
-		// Return cache data pointer to the caller
-		r_cache_data = &cache_data[cache_idx];
+	if (cached) {
+		int *cache_data = (int *)p_cache_data;
 
 		cache_idx += 4;
 
@@ -99,96 +91,92 @@ bool xatlas_mesh_lightmap_unwrap_callback(float p_texel_size, const float *p_ver
 		// Load vertices
 		*r_vertex_count = cache_data[cache_idx];
 		cache_idx++;
-		*r_vertices = &cache_data[cache_idx];
+		*r_vertex = &cache_data[cache_idx];
 		cache_idx += *r_vertex_count;
 
 		// Load UVs
-		*r_uvs = (float *)&cache_data[cache_idx];
+		*r_uv = (float *)&cache_data[cache_idx];
 		cache_idx += *r_vertex_count * 2;
 
 		// Load indices
 		*r_index_count = cache_data[cache_idx];
 		cache_idx++;
-		*r_indices = &cache_data[cache_idx];
-
-		// Return cache data size to the caller
-		r_cache_size = sizeof(int) * (4 + 2 + 1 + *r_vertex_count + (*r_vertex_count * 2) + 1 + *r_index_count); // hash + size hint + vertex_count + vertices + uvs + index_count + indices
-		r_used_cache = true;
-		return true;
-	}
-
-	//set up input mesh
-	xatlas::MeshDecl input_mesh;
-	input_mesh.indexData = p_indices;
-	input_mesh.indexCount = p_index_count;
-	input_mesh.indexFormat = xatlas::IndexFormat::UInt32;
-
-	input_mesh.vertexCount = p_vertex_count;
-	input_mesh.vertexPositionData = p_vertices;
-	input_mesh.vertexPositionStride = sizeof(float) * 3;
-	input_mesh.vertexNormalData = p_normals;
-	input_mesh.vertexNormalStride = sizeof(uint32_t) * 3;
-	input_mesh.vertexUvData = nullptr;
-	input_mesh.vertexUvStride = 0;
-
-	xatlas::ChartOptions chart_options;
-	xatlas::PackOptions pack_options;
-
-	pack_options.maxChartSize = 4096;
-	pack_options.blockAlign = true;
-	pack_options.padding = 1;
-	pack_options.texelsPerUnit = 1.0 / p_texel_size;
+		*r_index = &cache_data[cache_idx];
+	} else {
+		// set up input mesh
+		xatlas::MeshDecl input_mesh;
+		input_mesh.indexData = p_indices;
+		input_mesh.indexCount = p_index_count;
+		input_mesh.indexFormat = xatlas::IndexFormat::UInt32;
+
+		input_mesh.vertexCount = p_vertex_count;
+		input_mesh.vertexPositionData = p_vertices;
+		input_mesh.vertexPositionStride = sizeof(float) * 3;
+		input_mesh.vertexNormalData = p_normals;
+		input_mesh.vertexNormalStride = sizeof(uint32_t) * 3;
+		input_mesh.vertexUvData = NULL;
+		input_mesh.vertexUvStride = 0;
+
+		xatlas::ChartOptions chart_options;
+		chart_options.fixWinding = true;
+
+		xatlas::PackOptions pack_options;
+		pack_options.padding = 1;
+		pack_options.maxChartSize = 4094; // Lightmap atlassing needs 2 for padding between meshes, so 4096-2
+		pack_options.blockAlign = true;
+		pack_options.texelsPerUnit = 1.0 / p_texel_size;
+
+		xatlas::Atlas *atlas = xatlas::Create();
+
+		xatlas::AddMeshError err = xatlas::AddMesh(atlas, input_mesh, 1);
+		ERR_FAIL_COND_V_MSG(err != xatlas::AddMeshError::Success, false, xatlas::StringForEnum(err));
+
+		xatlas::Generate(atlas, chart_options, pack_options);
+
+		*r_size_hint_x = atlas->width;
+		*r_size_hint_y = atlas->height;
+
+		float w = *r_size_hint_x;
+		float h = *r_size_hint_y;
+
+		if (w == 0 || h == 0) {
+			xatlas::Destroy(atlas);
+			return false; //could not bake because there is no area
+		}
 
-	xatlas::Atlas *atlas = xatlas::Create();
-	printf("Adding mesh..\n");
-	xatlas::AddMeshError err = xatlas::AddMesh(atlas, input_mesh, 1);
-	ERR_FAIL_COND_V_MSG(err != xatlas::AddMeshError::Success, false, xatlas::StringForEnum(err));
+		const xatlas::Mesh &output = atlas->meshes[0];
+
+		*r_vertex = (int *)memalloc(sizeof(int) * output.vertexCount);
+		ERR_FAIL_NULL_V_MSG(*r_vertex, false, "Out of memory.");
+		*r_uv = (float *)memalloc(sizeof(float) * output.vertexCount * 2);
+		ERR_FAIL_NULL_V_MSG(*r_uv, false, "Out of memory.");
+		*r_index = (int *)memalloc(sizeof(int) * output.indexCount);
+		ERR_FAIL_NULL_V_MSG(*r_index, false, "Out of memory.");
+
+		float max_x = 0;
+		float max_y = 0;
+		for (uint32_t i = 0; i < output.vertexCount; i++) {
+			(*r_vertex)[i] = output.vertexArray[i].xref;
+			(*r_uv)[i * 2 + 0] = output.vertexArray[i].uv[0] / w;
+			(*r_uv)[i * 2 + 1] = output.vertexArray[i].uv[1] / h;
+			max_x = MAX(max_x, output.vertexArray[i].uv[0]);
+			max_y = MAX(max_y, output.vertexArray[i].uv[1]);
+		}
 
-	printf("Generate..\n");
-	xatlas::Generate(atlas, chart_options, pack_options);
+		*r_vertex_count = output.vertexCount;
 
-	*r_size_hint_x = atlas->width;
-	*r_size_hint_y = atlas->height;
+		for (uint32_t i = 0; i < output.indexCount; i++) {
+			(*r_index)[i] = output.indexArray[i];
+		}
 
-	float w = *r_size_hint_x;
-	float h = *r_size_hint_y;
+		*r_index_count = output.indexCount;
 
-	if (w == 0 || h == 0) {
 		xatlas::Destroy(atlas);
-		return false; //could not bake because there is no area
-	}
-
-	const xatlas::Mesh &output = atlas->meshes[0];
-
-	*r_vertices = (int *)malloc(sizeof(int) * output.vertexCount);
-	ERR_FAIL_NULL_V_MSG(*r_vertices, false, "Out of memory.");
-	*r_uvs = (float *)malloc(sizeof(float) * output.vertexCount * 2);
-	ERR_FAIL_NULL_V_MSG(*r_uvs, false, "Out of memory.");
-	*r_indices = (int *)malloc(sizeof(int) * output.indexCount);
-	ERR_FAIL_NULL_V_MSG(*r_indices, false, "Out of memory.");
-
-	float max_x = 0.0;
-	float max_y = 0.0;
-	for (uint32_t i = 0; i < output.vertexCount; i++) {
-		(*r_vertices)[i] = output.vertexArray[i].xref;
-		(*r_uvs)[i * 2 + 0] = output.vertexArray[i].uv[0] / w;
-		(*r_uvs)[i * 2 + 1] = output.vertexArray[i].uv[1] / h;
-		max_x = MAX(max_x, output.vertexArray[i].uv[0]);
-		max_y = MAX(max_y, output.vertexArray[i].uv[1]);
 	}
 
-	printf("Final texture size: %f,%f - max %f,%f\n", w, h, max_x, max_y);
-	*r_vertex_count = output.vertexCount;
+	if (*r_use_cache) {
+		// Build cache data for current mesh
 
-	for (uint32_t i = 0; i < output.indexCount; i++) {
-		(*r_indices)[i] = output.indexArray[i];
-	}
-
-	*r_index_count = output.indexCount;
-
-	xatlas::Destroy(atlas);
-
-	if (r_used_cache) {
 		unsigned int new_cache_size = 4 + 2 + 1 + *r_vertex_count + (*r_vertex_count * 2) + 1 + *r_index_count; // hash + size hint + vertex_count + vertices + uvs + index_count + indices
 		new_cache_size *= sizeof(int);
 		int *new_cache_data = (int *)memalloc(new_cache_size);
@@ -208,11 +196,11 @@ bool xatlas_mesh_lightmap_unwrap_callback(float p_texel_size, const float *p_ver
 		new_cache_idx++;
 
 		// vertices
-		memcpy(&new_cache_data[new_cache_idx], *r_vertices, sizeof(int) * *r_vertex_count);
+		memcpy(&new_cache_data[new_cache_idx], *r_vertex, sizeof(int) * (*r_vertex_count));
 		new_cache_idx += *r_vertex_count;
 
 		// uvs
-		memcpy(&new_cache_data[new_cache_idx], *r_uvs, sizeof(float) * *r_vertex_count * 2);
+		memcpy(&new_cache_data[new_cache_idx], *r_uv, sizeof(float) * (*r_vertex_count) * 2);
 		new_cache_idx += *r_vertex_count * 2;
 
 		// index count
@@ -220,15 +208,15 @@ bool xatlas_mesh_lightmap_unwrap_callback(float p_texel_size, const float *p_ver
 		new_cache_idx++;
 
 		// indices
-		memcpy(&new_cache_data[new_cache_idx], *r_indices, sizeof(int) * *r_index_count);
-		new_cache_idx += *r_index_count;
+		memcpy(&new_cache_data[new_cache_idx], *r_index, sizeof(int) * (*r_index_count));
 
 		// Return cache data to the caller
-		r_cache_data = new_cache_data;
-		r_cache_size = new_cache_size;
-		r_used_cache = false;
+		*r_mesh_cache = (uint8_t *)new_cache_data;
+		*r_mesh_cache_size = new_cache_size;
 	}
 
+	*r_use_cache = cached; // Return whether cache was used.
+
 	return true;
 }
 

+ 74 - 60
scene/resources/mesh.cpp

@@ -1401,7 +1401,7 @@ void ArrayMesh::regen_normal_maps() {
 }
 
 //dirty hack
-bool (*array_mesh_lightmap_unwrap_callback)(float p_texel_size, const float *p_vertices, const float *p_normals, int p_vertex_count, const int *p_indices, int p_index_count, float **r_uv, int **r_vertex, int *r_vertex_count, int **r_index, int *r_index_count, int *r_size_hint_x, int *r_size_hint_y, int *&r_cache_data, unsigned int &r_cache_size, bool &r_used_cache);
+bool (*array_mesh_lightmap_unwrap_callback)(float p_texel_size, const float *p_vertices, const float *p_normals, int p_vertex_count, const int *p_indices, int p_index_count, const uint8_t *p_cache_data, bool *r_use_cache, uint8_t **r_mesh_cache, int *r_mesh_cache_size, float **r_uv, int **r_vertex, int *r_vertex_count, int **r_index, int *r_index_count, int *r_size_hint_x, int *r_size_hint_y) = NULL;
 
 struct ArrayMeshLightmapSurface {
 	Ref<Material> material;
@@ -1411,28 +1411,28 @@ struct ArrayMeshLightmapSurface {
 };
 
 Error ArrayMesh::lightmap_unwrap(const Transform &p_base_transform, float p_texel_size) {
-	int *cache_data = nullptr;
-	unsigned int cache_size = 0;
-	bool use_cache = false; // Don't use cache
-	return lightmap_unwrap_cached(cache_data, cache_size, use_cache, p_base_transform, p_texel_size);
+	Vector<uint8_t> null_cache;
+	return lightmap_unwrap_cached(p_base_transform, p_texel_size, null_cache, null_cache, false);
 }
 
-Error ArrayMesh::lightmap_unwrap_cached(int *&r_cache_data, unsigned int &r_cache_size, bool &r_used_cache, const Transform &p_base_transform, float p_texel_size) {
+Error ArrayMesh::lightmap_unwrap_cached(const Transform &p_base_transform, float p_texel_size, const Vector<uint8_t> &p_src_cache, Vector<uint8_t> &r_dst_cache, bool p_generate_cache) {
 	ERR_FAIL_COND_V(!array_mesh_lightmap_unwrap_callback, ERR_UNCONFIGURED);
 	ERR_FAIL_COND_V_MSG(blend_shapes.size() != 0, ERR_UNAVAILABLE, "Can't unwrap mesh with blend shapes.");
 
-	Vector<float> vertices;
-	Vector<float> normals;
-	Vector<int> indices;
-	Vector<float> uv;
-	Vector<Pair<int, int>> uv_indices;
+	LocalVector<float> vertices;
+	LocalVector<float> normals;
+	LocalVector<int> indices;
+	LocalVector<float> uv;
+	LocalVector<Pair<int, int>> uv_indices;
 
 	Vector<ArrayMeshLightmapSurface> lightmap_surfaces;
 
 	// Keep only the scale
-	Transform transform = p_base_transform;
-	transform.origin = Vector3();
-	transform.looking_at(Vector3(1, 0, 0), Vector3(0, 1, 0));
+	Basis basis = p_base_transform.get_basis();
+	Vector3 scale = Vector3(basis.get_axis(0).length(), basis.get_axis(1).length(), basis.get_axis(2).length());
+
+	Transform transform;
+	transform.scale(scale);
 
 	Basis normal_basis = transform.basis.inverse().transposed();
 
@@ -1446,14 +1446,12 @@ Error ArrayMesh::lightmap_unwrap_cached(int *&r_cache_data, unsigned int &r_cach
 
 		Array arrays = surface_get_arrays(i);
 		s.material = surface_get_material(i);
-		SurfaceTool::create_vertex_array_from_triangle_arrays(arrays, s.vertices);
+		SurfaceTool::create_vertex_array_from_triangle_arrays(arrays, s.vertices, &s.format);
 
-		Vector<Vector3> rvertices = arrays[Mesh::ARRAY_VERTEX];
+		PackedVector3Array rvertices = arrays[Mesh::ARRAY_VERTEX];
 		int vc = rvertices.size();
-		const Vector3 *r = rvertices.ptr();
 
-		Vector<Vector3> rnormals = arrays[Mesh::ARRAY_NORMAL];
-		const Vector3 *rn = rnormals.ptr();
+		PackedVector3Array rnormals = arrays[Mesh::ARRAY_NORMAL];
 
 		int vertex_ofs = vertices.size() / 3;
 
@@ -1462,24 +1460,29 @@ Error ArrayMesh::lightmap_unwrap_cached(int *&r_cache_data, unsigned int &r_cach
 		uv_indices.resize(vertex_ofs + vc);
 
 		for (int j = 0; j < vc; j++) {
-			Vector3 v = transform.xform(r[j]);
-			Vector3 n = normal_basis.xform(rn[j]).normalized();
-
-			vertices.write[(j + vertex_ofs) * 3 + 0] = v.x;
-			vertices.write[(j + vertex_ofs) * 3 + 1] = v.y;
-			vertices.write[(j + vertex_ofs) * 3 + 2] = v.z;
-			normals.write[(j + vertex_ofs) * 3 + 0] = n.x;
-			normals.write[(j + vertex_ofs) * 3 + 1] = n.y;
-			normals.write[(j + vertex_ofs) * 3 + 2] = n.z;
-			uv_indices.write[j + vertex_ofs] = Pair<int, int>(i, j);
+			Vector3 v = transform.xform(rvertices[j]);
+			Vector3 n = normal_basis.xform(rnormals[j]).normalized();
+
+			vertices[(j + vertex_ofs) * 3 + 0] = v.x;
+			vertices[(j + vertex_ofs) * 3 + 1] = v.y;
+			vertices[(j + vertex_ofs) * 3 + 2] = v.z;
+			normals[(j + vertex_ofs) * 3 + 0] = n.x;
+			normals[(j + vertex_ofs) * 3 + 1] = n.y;
+			normals[(j + vertex_ofs) * 3 + 2] = n.z;
+			uv_indices[j + vertex_ofs] = Pair<int, int>(i, j);
 		}
 
-		Vector<int> rindices = arrays[Mesh::ARRAY_INDEX];
+		PackedInt32Array rindices = arrays[Mesh::ARRAY_INDEX];
 		int ic = rindices.size();
 
+		float eps = 1.19209290e-7F; // Taken from xatlas.h
 		if (ic == 0) {
 			for (int j = 0; j < vc / 3; j++) {
-				if (Face3(r[j * 3 + 0], r[j * 3 + 1], r[j * 3 + 2]).is_degenerate()) {
+				Vector3 p0 = transform.xform(rvertices[j * 3 + 0]);
+				Vector3 p1 = transform.xform(rvertices[j * 3 + 1]);
+				Vector3 p2 = transform.xform(rvertices[j * 3 + 2]);
+
+				if ((p0 - p1).length_squared() < eps || (p1 - p2).length_squared() < eps || (p2 - p0).length_squared() < eps) {
 					continue;
 				}
 
@@ -1489,15 +1492,18 @@ Error ArrayMesh::lightmap_unwrap_cached(int *&r_cache_data, unsigned int &r_cach
 			}
 
 		} else {
-			const int *ri = rindices.ptr();
-
 			for (int j = 0; j < ic / 3; j++) {
-				if (Face3(r[ri[j * 3 + 0]], r[ri[j * 3 + 1]], r[ri[j * 3 + 2]]).is_degenerate()) {
+				Vector3 p0 = transform.xform(rvertices[rindices[j * 3 + 0]]);
+				Vector3 p1 = transform.xform(rvertices[rindices[j * 3 + 1]]);
+				Vector3 p2 = transform.xform(rvertices[rindices[j * 3 + 2]]);
+
+				if ((p0 - p1).length_squared() < eps || (p1 - p2).length_squared() < eps || (p2 - p0).length_squared() < eps) {
 					continue;
 				}
-				indices.push_back(vertex_ofs + ri[j * 3 + 0]);
-				indices.push_back(vertex_ofs + ri[j * 3 + 1]);
-				indices.push_back(vertex_ofs + ri[j * 3 + 2]);
+
+				indices.push_back(vertex_ofs + rindices[j * 3 + 0]);
+				indices.push_back(vertex_ofs + rindices[j * 3 + 1]);
+				indices.push_back(vertex_ofs + rindices[j * 3 + 2]);
 			}
 		}
 
@@ -1506,6 +1512,9 @@ Error ArrayMesh::lightmap_unwrap_cached(int *&r_cache_data, unsigned int &r_cach
 
 	//unwrap
 
+	bool use_cache = p_generate_cache; // Used to request cache generation and to know if cache was used
+	uint8_t *gen_cache;
+	int gen_cache_size;
 	float *gen_uvs;
 	int *gen_vertices;
 	int *gen_indices;
@@ -1514,17 +1523,16 @@ Error ArrayMesh::lightmap_unwrap_cached(int *&r_cache_data, unsigned int &r_cach
 	int size_x;
 	int size_y;
 
-	bool ok = array_mesh_lightmap_unwrap_callback(p_texel_size, vertices.ptr(), normals.ptr(), vertices.size() / 3, indices.ptr(), indices.size(), &gen_uvs, &gen_vertices, &gen_vertex_count, &gen_indices, &gen_index_count, &size_x, &size_y, r_cache_data, r_cache_size, r_used_cache);
+	bool ok = array_mesh_lightmap_unwrap_callback(p_texel_size, vertices.ptr(), normals.ptr(), vertices.size() / 3, indices.ptr(), indices.size(), p_src_cache.ptr(), &use_cache, &gen_cache, &gen_cache_size, &gen_uvs, &gen_vertices, &gen_vertex_count, &gen_indices, &gen_index_count, &size_x, &size_y);
 
 	if (!ok) {
 		return ERR_CANT_CREATE;
 	}
 
-	//remove surfaces
 	clear_surfaces();
 
 	//create surfacetools for each surface..
-	Vector<Ref<SurfaceTool>> surfaces_tools;
+	LocalVector<Ref<SurfaceTool>> surfaces_tools;
 
 	for (int i = 0; i < lightmap_surfaces.size(); i++) {
 		Ref<SurfaceTool> st;
@@ -1535,11 +1543,12 @@ Error ArrayMesh::lightmap_unwrap_cached(int *&r_cache_data, unsigned int &r_cach
 	}
 
 	print_verbose("Mesh: Gen indices: " + itos(gen_index_count));
+
 	//go through all indices
 	for (int i = 0; i < gen_index_count; i += 3) {
-		ERR_FAIL_INDEX_V(gen_vertices[gen_indices[i + 0]], uv_indices.size(), ERR_BUG);
-		ERR_FAIL_INDEX_V(gen_vertices[gen_indices[i + 1]], uv_indices.size(), ERR_BUG);
-		ERR_FAIL_INDEX_V(gen_vertices[gen_indices[i + 2]], uv_indices.size(), ERR_BUG);
+		ERR_FAIL_INDEX_V(gen_vertices[gen_indices[i + 0]], (int)uv_indices.size(), ERR_BUG);
+		ERR_FAIL_INDEX_V(gen_vertices[gen_indices[i + 1]], (int)uv_indices.size(), ERR_BUG);
+		ERR_FAIL_INDEX_V(gen_vertices[gen_indices[i + 2]], (int)uv_indices.size(), ERR_BUG);
 
 		ERR_FAIL_COND_V(uv_indices[gen_vertices[gen_indices[i + 0]]].first != uv_indices[gen_vertices[gen_indices[i + 1]]].first || uv_indices[gen_vertices[gen_indices[i + 0]]].first != uv_indices[gen_vertices[gen_indices[i + 2]]].first, ERR_BUG);
 
@@ -1549,48 +1558,53 @@ Error ArrayMesh::lightmap_unwrap_cached(int *&r_cache_data, unsigned int &r_cach
 			SurfaceTool::Vertex v = lightmap_surfaces[surface].vertices[uv_indices[gen_vertices[gen_indices[i + j]]].second];
 
 			if (lightmap_surfaces[surface].format & ARRAY_FORMAT_COLOR) {
-				surfaces_tools.write[surface]->set_color(v.color);
+				surfaces_tools[surface]->set_color(v.color);
 			}
 			if (lightmap_surfaces[surface].format & ARRAY_FORMAT_TEX_UV) {
-				surfaces_tools.write[surface]->set_uv(v.uv);
+				surfaces_tools[surface]->set_uv(v.uv);
 			}
 			if (lightmap_surfaces[surface].format & ARRAY_FORMAT_NORMAL) {
-				surfaces_tools.write[surface]->set_normal(v.normal);
+				surfaces_tools[surface]->set_normal(v.normal);
 			}
 			if (lightmap_surfaces[surface].format & ARRAY_FORMAT_TANGENT) {
 				Plane t;
 				t.normal = v.tangent;
 				t.d = v.binormal.dot(v.normal.cross(v.tangent)) < 0 ? -1 : 1;
-				surfaces_tools.write[surface]->set_tangent(t);
+				surfaces_tools[surface]->set_tangent(t);
 			}
 			if (lightmap_surfaces[surface].format & ARRAY_FORMAT_BONES) {
-				surfaces_tools.write[surface]->set_bones(v.bones);
+				surfaces_tools[surface]->set_bones(v.bones);
 			}
 			if (lightmap_surfaces[surface].format & ARRAY_FORMAT_WEIGHTS) {
-				surfaces_tools.write[surface]->set_weights(v.weights);
+				surfaces_tools[surface]->set_weights(v.weights);
 			}
 
 			Vector2 uv2(gen_uvs[gen_indices[i + j] * 2 + 0], gen_uvs[gen_indices[i + j] * 2 + 1]);
-			surfaces_tools.write[surface]->set_uv2(uv2);
+			surfaces_tools[surface]->set_uv2(uv2);
 
-			surfaces_tools.write[surface]->add_vertex(v.vertex);
+			surfaces_tools[surface]->add_vertex(v.vertex);
 		}
 	}
 
 	//generate surfaces
-
-	for (int i = 0; i < surfaces_tools.size(); i++) {
-		surfaces_tools.write[i]->index();
-		surfaces_tools.write[i]->commit(Ref<ArrayMesh>((ArrayMesh *)this), lightmap_surfaces[i].format);
+	for (unsigned int i = 0; i < surfaces_tools.size(); i++) {
+		surfaces_tools[i]->index();
+		surfaces_tools[i]->commit(Ref<ArrayMesh>((ArrayMesh *)this), lightmap_surfaces[i].format);
 	}
 
 	set_lightmap_size_hint(Size2(size_x, size_y));
 
-	if (!r_used_cache) {
-		//free stuff
-		::free(gen_vertices);
-		::free(gen_indices);
-		::free(gen_uvs);
+	if (gen_cache_size > 0) {
+		r_dst_cache.resize(gen_cache_size);
+		memcpy(r_dst_cache.ptrw(), gen_cache, gen_cache_size);
+		memfree(gen_cache);
+	}
+
+	if (!use_cache) {
+		// Cache was not used, free the buffers
+		memfree(gen_vertices);
+		memfree(gen_indices);
+		memfree(gen_uvs);
 	}
 
 	return OK;

+ 1 - 1
scene/resources/mesh.h

@@ -263,7 +263,7 @@ public:
 	void regen_normal_maps();
 
 	Error lightmap_unwrap(const Transform &p_base_transform = Transform(), float p_texel_size = 0.05);
-	Error lightmap_unwrap_cached(int *&r_cache_data, unsigned int &r_cache_size, bool &r_used_cache, const Transform &p_base_transform = Transform(), float p_texel_size = 0.05);
+	Error lightmap_unwrap_cached(const Transform &p_base_transform, float p_texel_size, const Vector<uint8_t> &p_src_cache, Vector<uint8_t> &r_dst_cache, bool p_generate_cache = true);
 
 	virtual void reload_from_file() override;
 

+ 1 - 0
servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp

@@ -1698,6 +1698,7 @@ void RenderForwardClustered::_render_uv2(const PagedArray<GeometryInstance *> &p
 			_render_list(draw_list, RD::get_singleton()->framebuffer_get_format(p_framebuffer), &render_list_params, 0, render_list_params.element_count); //first wireframe, for pseudo conservative
 		}
 		render_list_params.uv_offset = Vector2();
+		render_list_params.force_wireframe = false;
 		_render_list(draw_list, RD::get_singleton()->framebuffer_get_format(p_framebuffer), &render_list_params, 0, render_list_params.element_count); //second regular triangles
 
 		RD::get_singleton()->draw_list_end();

+ 2 - 0
servers/rendering/renderer_rd/renderer_scene_render_rd.cpp

@@ -2554,6 +2554,7 @@ void RendererSceneRenderRD::_setup_lights(const PagedArray<RID> &p_lights, const
 
 					light_data.soft_shadow_scale = storage->light_get_param(base, RS::LIGHT_PARAM_SHADOW_BLUR);
 					light_data.softshadow_angle = angular_diameter;
+					light_data.bake_mode = storage->light_get_bake_mode(base);
 
 					if (angular_diameter <= 0.0) {
 						light_data.soft_shadow_scale *= directional_shadow_quality_radius_get(); // Only use quality radius for PCF
@@ -2621,6 +2622,7 @@ void RendererSceneRenderRD::_setup_lights(const PagedArray<RID> &p_lights, const
 		light_data.color[1] = linear_col.g * energy;
 		light_data.color[2] = linear_col.b * energy;
 		light_data.specular_amount = storage->light_get_param(base, RS::LIGHT_PARAM_SPECULAR) * 2.0;
+		light_data.bake_mode = storage->light_get_bake_mode(base);
 
 		float radius = MAX(0.001, storage->light_get_param(base, RS::LIGHT_PARAM_RANGE));
 		light_data.inv_radius = 1.0 / radius;

+ 3 - 2
servers/rendering/renderer_rd/renderer_scene_render_rd.h

@@ -497,7 +497,7 @@ private:
 			float soft_shadow_scale;
 			uint32_t mask;
 			float shadow_volumetric_fog_fade;
-			uint32_t pad;
+			uint32_t bake_mode;
 			float projector_rect[4];
 		};
 
@@ -514,7 +514,8 @@ private:
 			uint32_t shadow_enabled;
 			float fade_from;
 			float fade_to;
-			uint32_t pad[3];
+			uint32_t pad[2];
+			uint32_t bake_mode;
 			float shadow_volumetric_fog_fade;
 			float shadow_bias[4];
 			float shadow_normal_bias[4];

+ 6 - 2
servers/rendering/renderer_rd/shaders/light_data_inc.glsl

@@ -1,3 +1,6 @@
+#define LIGHT_BAKE_DISABLED 0
+#define LIGHT_BAKE_DYNAMIC 1
+#define LIGHT_BAKE_STATIC 2
 
 struct LightData { //this structure needs to be as packed as possible
 	vec3 position;
@@ -23,7 +26,7 @@ struct LightData { //this structure needs to be as packed as possible
 	float soft_shadow_scale; // scales the shadow kernel for blurrier shadows
 	uint mask;
 	float shadow_volumetric_fog_fade;
-	uint pad;
+	uint bake_mode;
 	vec4 projector_rect; //projector rect in srgb decal atlas
 };
 
@@ -60,7 +63,8 @@ struct DirectionalLightData {
 	bool shadow_enabled;
 	float fade_from;
 	float fade_to;
-	uvec3 pad;
+	uvec2 pad;
+	uint bake_mode;
 	float shadow_volumetric_fog_fade;
 	vec4 shadow_bias;
 	vec4 shadow_normal_bias;

+ 12 - 0
servers/rendering/renderer_rd/shaders/scene_forward_clustered.glsl

@@ -1227,6 +1227,10 @@ void main() {
 				continue; //not masked
 			}
 
+			if (directional_lights.data[i].bake_mode == LIGHT_BAKE_STATIC && bool(instances.data[instance_index].flags & INSTANCE_FLAGS_USE_LIGHTMAP)) {
+				continue; // Statically baked light and object uses lightmap, skip
+			}
+
 			float shadow = 1.0;
 
 #ifdef USE_SOFT_SHADOWS
@@ -1676,6 +1680,10 @@ void main() {
 						continue; //not masked
 					}
 
+					if (omni_lights.data[light_index].bake_mode == LIGHT_BAKE_STATIC && bool(instances.data[instance_index].flags & INSTANCE_FLAGS_USE_LIGHTMAP)) {
+						continue; // Statically baked light and object uses lightmap, skip
+					}
+
 					float shadow = light_process_omni_shadow(light_index, vertex, view);
 
 					shadow = blur_shadow(shadow);
@@ -1749,6 +1757,10 @@ void main() {
 						continue; //not masked
 					}
 
+					if (spot_lights.data[light_index].bake_mode == LIGHT_BAKE_STATIC && bool(instances.data[instance_index].flags & INSTANCE_FLAGS_USE_LIGHTMAP)) {
+						continue; // Statically baked light and object uses lightmap, skip
+					}
+
 					float shadow = light_process_spot_shadow(light_index, vertex, view);
 
 					shadow = blur_shadow(shadow);

+ 1 - 1
servers/rendering/renderer_scene_cull.cpp

@@ -2394,7 +2394,7 @@ void RendererSceneCull::_frustum_cull(CullData &cull_data, FrustumCullResult &cu
 				cull_result.gi_probes.push_back(RID::from_uint64(idata.instance_data_rid));
 
 			} else if (base_type == RS::INSTANCE_LIGHTMAP) {
-				cull_result.gi_probes.push_back(RID::from_uint64(idata.instance_data_rid));
+				cull_result.lightmaps.push_back(RID::from_uint64(idata.instance_data_rid));
 			} else if (((1 << base_type) & RS::INSTANCE_GEOMETRY_MASK) && !(idata.flags & InstanceData::FLAG_CAST_SHADOWS_ONLY)) {
 				bool keep = true;
 

+ 2 - 2
servers/rendering_server.cpp

@@ -472,8 +472,8 @@ Error RenderingServer::_surface_set_data(Array p_arrays, uint32_t p_format, uint
 				const Vector2 *src = array.ptr();
 
 				for (int i = 0; i < p_vertex_array_len; i++) {
-					uint16_t uv[2] = { Math::make_half_float(src[i].x), Math::make_half_float(src[i].y) };
-					memcpy(&aw[p_offsets[ai] + i * p_attrib_stride], uv, 2 * 2);
+					float uv[2] = { src[i].x, src[i].y };
+					memcpy(&aw[p_offsets[ai] + i * p_attrib_stride], uv, 2 * 4);
 				}
 			} break;
 			case RS::ARRAY_CUSTOM0:

+ 54 - 53
thirdparty/oidn/core/transfer_function.cpp

@@ -24,10 +24,6 @@ namespace oidn {
   float AutoexposureNode::autoexposure(const Image& color)
   {
     assert(color.format == Format::Float3);
-// -- GODOT start --
-// We don't want to mess with TTB and we don't use autoexposure, so we disable this code
-#if 0
-// -- GODOT end --
 
     constexpr float key = 0.18f;
     constexpr float eps = 1e-8f;
@@ -42,61 +38,66 @@ namespace oidn {
     // Compute the average log luminance of the downsampled image
     using Sum = std::pair<float, int>;
 
-    Sum sum =
-      tbb::parallel_reduce(
-        tbb::blocked_range2d<int>(0, HK, 0, WK),
-        Sum(0.f, 0),
-        [&](const tbb::blocked_range2d<int>& r, Sum sum) -> Sum
+    // -- GODOT start --
+    // Sum sum =
+    //   tbb::parallel_reduce(
+    //     tbb::blocked_range2d<int>(0, HK, 0, WK),
+    //     Sum(0.f, 0),
+    //     [&](const tbb::blocked_range2d<int>& r, Sum sum) -> Sum
+    //     {
+    //       // Iterate over blocks
+    //       for (int i = r.rows().begin(); i != r.rows().end(); ++i)
+    //       {
+    //         for (int j = r.cols().begin(); j != r.cols().end(); ++j)
+    //         {
+
+    Sum sum = Sum(0.0f, 0);
+
+    for (int i = 0; i != HK; ++i)
+    {
+      for (int j = 0; j != WK; ++j)
+      {
+        // Compute the average luminance in the current block
+        const int beginH = int(ptrdiff_t(i)   * H / HK);
+        const int beginW = int(ptrdiff_t(j)   * W / WK);
+        const int endH   = int(ptrdiff_t(i+1) * H / HK);
+        const int endW   = int(ptrdiff_t(j+1) * W / WK);
+
+        float L = 0.f;
+
+        for (int h = beginH; h < endH; ++h)
         {
-          // Iterate over blocks
-          for (int i = r.rows().begin(); i != r.rows().end(); ++i)
+          for (int w = beginW; w < endW; ++w)
           {
-            for (int j = r.cols().begin(); j != r.cols().end(); ++j)
-            {
-              // Compute the average luminance in the current block
-              const int beginH = int(ptrdiff_t(i)   * H / HK);
-              const int beginW = int(ptrdiff_t(j)   * W / WK);
-              const int endH   = int(ptrdiff_t(i+1) * H / HK);
-              const int endW   = int(ptrdiff_t(j+1) * W / WK);
-
-              float L = 0.f;
-
-              for (int h = beginH; h < endH; ++h)
-              {
-                for (int w = beginW; w < endW; ++w)
-                {
-                  const float* rgb = (const float*)color.get(h, w);
-
-                  const float r = maxSafe(rgb[0], 0.f);
-                  const float g = maxSafe(rgb[1], 0.f);
-                  const float b = maxSafe(rgb[2], 0.f);
-
-                  L += luminance(r, g, b);
-                }
-              }
-
-              L /= (endH - beginH) * (endW - beginW);
-
-              // Accumulate the log luminance
-              if (L > eps)
-              {
-                sum.first += log2(L);
-                sum.second++;
-              }
-            }
+            const float* rgb = (const float*)color.get(h, w);
+
+            const float r = maxSafe(rgb[0], 0.f);
+            const float g = maxSafe(rgb[1], 0.f);
+            const float b = maxSafe(rgb[2], 0.f);
+
+            L += luminance(r, g, b);
           }
+        }
 
-          return sum;
-        },
-        [](Sum a, Sum b) -> Sum { return Sum(a.first+b.first, a.second+b.second); },
-        tbb::static_partitioner()
-      );
+        L /= (endH - beginH) * (endW - beginW);
+
+        // Accumulate the log luminance
+        if (L > eps)
+        {
+          sum.first += log2(L);
+          sum.second++;
+        }
+      }
+    }
+
+    //     return sum;
+    //   },
+    //   [](Sum a, Sum b) -> Sum { return Sum(a.first+b.first, a.second+b.second); },
+    //   tbb::static_partitioner()
+    // );
+    // -- GODOT end --
 
     return (sum.second > 0) ? (key / exp2(sum.first / float(sum.second))) : 1.f;
-// -- GODOT start --
-#endif
-    return 1.0;
-// -- GODOT end --
   }
 
 } // namespace oidn

+ 49 - 19
thirdparty/oidn/patches/godot-changes-c58c5216.patch

@@ -280,28 +280,58 @@ index 8c2de09..ed8328c 100644
  namespace oidn {
  
 diff --git a/core/transfer_function.cpp b/core/transfer_function.cpp
-index 601f814..487f0a9 100644
+index 601f814..ce5deca 100644
 --- a/core/transfer_function.cpp
 +++ b/core/transfer_function.cpp
-@@ -24,6 +24,10 @@ namespace oidn {
-   float AutoexposureNode::autoexposure(const Image& color)
-   {
-     assert(color.format == Format::Float3);
-+// -- GODOT start --
-+// We don't want to mess with TTB and we don't use autoexposure, so we disable this code
-+#if 0
-+// -- GODOT end --
+@@ -38,16 +38,24 @@ namespace oidn {
+     // Compute the average log luminance of the downsampled image
+     using Sum = std::pair<float, int>;
+ 
+-    Sum sum =
+-      tbb::parallel_reduce(
+-        tbb::blocked_range2d<int>(0, HK, 0, WK),
+-        Sum(0.f, 0),
+-        [&](const tbb::blocked_range2d<int>& r, Sum sum) -> Sum
++    // -- GODOT start --
++    // Sum sum =
++    //   tbb::parallel_reduce(
++    //     tbb::blocked_range2d<int>(0, HK, 0, WK),
++    //     Sum(0.f, 0),
++    //     [&](const tbb::blocked_range2d<int>& r, Sum sum) -> Sum
++    //     {
++    //       // Iterate over blocks
++    //       for (int i = r.rows().begin(); i != r.rows().end(); ++i)
++    //       {
++    //         for (int j = r.cols().begin(); j != r.cols().end(); ++j)
++    //         {
++
++    Sum sum = Sum(0.0f, 0);
++
++    for (int i = 0; i != HK; ++i)
+     {
+-          // Iterate over blocks
+-          for (int i = r.rows().begin(); i != r.rows().end(); ++i)
+-          {
+-            for (int j = r.cols().begin(); j != r.cols().end(); ++j)
++      for (int j = 0; j != WK; ++j)
+       {
+         // Compute the average luminance in the current block
+         const int beginH = int(ptrdiff_t(i)   * H / HK);
+@@ -82,11 +90,12 @@ namespace oidn {
+       }
+     }
  
-     constexpr float key = 0.18f;
-     constexpr float eps = 1e-8f;
-@@ -89,6 +93,10 @@ namespace oidn {
-       );
+-          return sum;
+-        },
+-        [](Sum a, Sum b) -> Sum { return Sum(a.first+b.first, a.second+b.second); },
+-        tbb::static_partitioner()
+-      );
++    //     return sum;
++    //   },
++    //   [](Sum a, Sum b) -> Sum { return Sum(a.first+b.first, a.second+b.second); },
++    //   tbb::static_partitioner()
++    // );
++    // -- GODOT end --
  
      return (sum.second > 0) ? (key / exp2(sum.first / float(sum.second))) : 1.f;
-+// -- GODOT start --
-+#endif
-+    return 1.0;
-+// -- GODOT end --
    }
- 
- } // namespace oidn