#include "lhelper.h" #include "ldebug.h" #include "scene/3d/baked_lightmap.h" #include "scene/3d/mesh_instance.h" #include "thirdparty/xatlas/xatlas.h" // for ::free #include LHelper::LHelper() { SetUnMergeParams(0.1f, 0.95f); } String LHelper::LFace::ToString() const { String sz; for (int c=0; c<3; c++) { sz += String(Variant(m_Pos[c])); sz += ", "; } sz += "norm : "; for (int c=0; c<3; c++) { sz += String(Variant(m_Norm[c])); sz += ", "; } return sz; } bool LHelper::LVert::ApproxEqual(const LVert &o) const { if (m_Pos != o.m_Pos) return false; if (m_Norm != o.m_Norm) return false; if (m_UV != o.m_UV) return false; if (m_UV2 != o.m_UV2) return false; return true; } ////////////////////////////////////////// /* bool LHelper::UnMergeSOBs(LRoomManager &manager, const PoolVector &uv2s) { int uv_count = 0; // go through each sob mesh for (int n=0; n(pGI); if (!pMI) continue; // if (UnMerge_SOB(*pMI, uv2s, uv_count) == false) // return false; } return true; } */ // main function for getting merged uv2 back to sobs bool LHelper::TransferUV2(const MeshInstance &mi_from, MeshInstance &mi_to) { LMerged merged; if (!FillMergedFromMesh(merged, mi_from)) return false; return UnMerge_SOB(mi_to, merged); } bool LHelper::FillMergedFromMesh(LMerged &merged, const MeshInstance &mesh) { Ref rmesh = mesh.get_mesh(); Array arrays = rmesh->surface_get_arrays(0); merged.m_Verts = arrays[VS::ARRAY_VERTEX]; merged.m_Norms = arrays[VS::ARRAY_NORMAL]; merged.m_UV2s = arrays[VS::ARRAY_TEX_UV2]; merged.m_Inds = arrays[VS::ARRAY_INDEX]; // PoolVector p_UV1s = arrays[VS::ARRAY_TEX_UV]; merged.m_nFaces = merged.m_Inds.size() / 3; if (merged.m_UV2s.size() == 0) { LPRINT(5, "Merged mesh has no secondary UVs, using primary UVs"); merged.m_UV2s = arrays[VS::ARRAY_TEX_UV]; if (merged.m_UV2s.size() == 0) { LWARN(5, "Merged mesh has no UVs"); return false; } } int miCount = 0; for (int mf=0; mf(pGI); if (!pMI) continue; if (UnMerge_SOB(*pMI, merged) == false) return false; } return true; } int LHelper::DebugCountUVs(MeshInstance &mi) { Ref rmesh = mi.get_mesh(); Array arrays = rmesh->surface_get_arrays(0); PoolVector verts = arrays[VS::ARRAY_VERTEX]; PoolVector uv1s = arrays[VS::ARRAY_TEX_UV]; PoolVector uv2s = arrays[VS::ARRAY_TEX_UV2]; LPRINT(5, "Lightmap num verts is " + itos (verts.size()) + "\tUV1s is " + itos (uv1s.size()) + "\tUV2s is " + itos(uv2s.size())); return uv2s.size(); } unsigned int LHelper::FindMatchingVertex(const PoolVector &uvs, const Vector2 &uv1) const { // very slow and inefficient .. thanks xatlas! for (int n=0; n thresh_diff) { #ifdef LDEBUG_UNMERGE if (bDebug) LPRINT(5, "\t\t\t\tRejecting x_diff " + ftos(x_diff)); #endif return false; } float z_diff = fabs (b_pos.z - a_pos.z); if (z_diff > thresh_diff) { #ifdef LDEBUG_UNMERGE if (bDebug) LPRINT(5, "\t\t\t\tRejecting z_diff " + ftos(z_diff)); #endif return false; } float y_diff = fabs (b_pos.y - a_pos.y); if (y_diff > thresh_diff) { #ifdef LDEBUG_UNMERGE if (bDebug) LPRINT(5, "\t\t\t\tRejecting y_diff " + ftos(y_diff)); #endif return false; } Vector3 pos_diff = b_pos - a_pos; if (pos_diff.length_squared() > thresh_diff_squared) // 0.1 { #ifdef LDEBUG_UNMERGE if (bDebug) LPRINT(5, "\t\t\t\tRejecting length squared " + ftos(pos_diff.length_squared())); #endif return false; } // make sure both are normalized Vector3 na = a_norm;//.normalized(); Vector3 nb = b_norm;//.normalized(); float norm_dot = na.dot(nb); if (norm_dot < m_MergeParams.m_fThresholdDot) { #ifdef LDEBUG_UNMERGE if (bDebug) LPRINT(5, "\t\t\t\tRejecting normal " + ftos(norm_dot) + " na : " + String(na) + ", nb : " + String(nb)); #endif return false; } return true; } int LHelper::DoFacesMatch_Offset(const LFace& sob_f, const LFace &m_face, int offset) const { #ifdef LDEBUG_UNMERGE // debug String sz = "\t\tPOSS match sob : "; sz += sob_f.ToString(); sz += "\n\t\t\tmerged : "; sz += m_face.ToString(); LPRINT(2, sz); #endif // does 2nd and third match? int offset1 = (offset + 1) % 3; if (!DoFaceVertsApproxMatch(sob_f, m_face, 1, offset1, true)) return -1; int offset2 = (offset + 2) % 3; if (!DoFaceVertsApproxMatch(sob_f, m_face, 2, offset2, true)) return -1; return offset; } // -1 for no match, or 0 for 0 offset match, 1 for +1, 2 for +2 offset match... int LHelper::DoFacesMatch(const LFace& sob_f, const LFace &m_face) const { // match one for (int offset = 0; offset < 3; offset++) { if (DoFaceVertsApproxMatch(sob_f, m_face, 0, offset, false)) { int res = DoFacesMatch_Offset(sob_f, m_face, offset); if (res != -1) return res; } } return -1; // no match } int LHelper::FindOrAddVert(LVector &uni_verts, const LVert &vert) const { for (int n=0; n merged_verts, const PoolVector merged_norms, const PoolVector &merged_uv2s, const PoolVector &merged_inds, int &vert_count) bool LHelper::UnMerge_SOB(MeshInstance &mi, LMerged &merged) { //LPRINT(2, "UnMerge_SOB " + mi.get_name()); Ref rmesh = mi.get_mesh(); Array arrays = rmesh->surface_get_arrays(0); PoolVector verts = arrays[VS::ARRAY_VERTEX]; PoolVector norms = arrays[VS::ARRAY_NORMAL]; PoolVector uv1s = arrays[VS::ARRAY_TEX_UV]; PoolVector inds = arrays[VS::ARRAY_INDEX]; // we need to get the vert positions and normals from local space to world space to match up with the // world space coords in the merged mesh PoolVector world_verts; PoolVector world_norms; Transform trans = mi.get_global_transform(); Transform_Verts(verts, world_verts, trans); Transform_Norms(norms, world_norms, trans); // these are the uvs to be filled in the sob PoolVector uv2s; uv2s.resize(verts.size()); // for each face in the SOB, attempt to find matching face in the merged mesh int nFaces = inds.size() / 3; int iCount = 0; int nMergedFaces = merged.m_nFaces; iCount = 0; // the number of unique verts in the UV2 mapped mesh may be HIGHER // than the original mesh, because verts with same pos / norm / uv may now have // different UV2. So we will be recreating the entire // mesh data with a new set of unique verts LVector UniqueVerts; PoolVector UniqueIndices; for (int f=0; f merged_uv2s.size()) // { // LWARN(5, "Lightmap and SOBs out of sync, num verts in mesh " + itos(verts.size()) + " total uvs " + itos(merged_uv2s.size())); // return false; // } // uv2 sub array // PoolVector uv2s; // for (int n=0; n unique_poss; PoolVector unique_norms; PoolVector unique_uv1s; PoolVector unique_uv2s; for (int n=0; n norms = arrays[VS::ARRAY_NORMAL]; // PoolVector uv1s = arrays[VS::ARRAY_TEX_UV]; // PoolVector inds = arrays[VS::ARRAY_INDEX]; Ref mat = mi.get_surface_material(0); Ref am_new; am_new.instance(); Array arr; arr.resize(Mesh::ARRAY_MAX); // arr[Mesh::ARRAY_VERTEX] = verts; // arr[Mesh::ARRAY_NORMAL] = norms; // arr[Mesh::ARRAY_INDEX] = inds; // arr[Mesh::ARRAY_TEX_UV] = uv1s; // arr[Mesh::ARRAY_TEX_UV2] = uv2s; arr[Mesh::ARRAY_VERTEX] = unique_poss; arr[Mesh::ARRAY_NORMAL] = unique_norms; arr[Mesh::ARRAY_INDEX] = UniqueIndices; arr[Mesh::ARRAY_TEX_UV] = unique_uv1s; arr[Mesh::ARRAY_TEX_UV2] = unique_uv2s; am_new->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, arr); // am_new->surface_set_material(0, mat); // hopefully the old mesh will be ref count freed? ??? mi.set_mesh(am_new); mi.set_surface_material(0, mat); return true; } //bool LHelper::CreateLightmapProxy(LRoomManager &manager, BakedLightmap &baked_lightmap) MeshInstance * LHelper::CreateLightmapProxy(LRoomManager &manager) { // create a temporary mesh instance to merge to, lightmap etc MeshInstance * pMerged = memnew(MeshInstance); pMerged->set_name("lightmap_proxy"); // baked_lightmap.add_child(pMerged); manager.get_parent()->add_child(pMerged); bool res = true; // create uv2s is done in merge if (!MergeSOBs(manager, pMerged)) { res = false; goto finish; } /* if (!BakeLightmap(baked_lightmap, pMerged)) { res = false; goto finish; } */ // unmerge the mesh back to the sobs if (!UnMergeSOBs(manager, pMerged)) { res = false; goto finish; } finish: // finished .. remove the merged child and delete // pMerged->queue_delete(); if (res) return pMerged; // failed pMerged->queue_delete(); return 0; // return res; } bool LHelper::MergeSOBs(LRoomManager &manager, MeshInstance * pMerged, bool bLightmapUnwrap) { PoolVector verts; PoolVector normals; PoolVector inds; // PoolVector uv2s; // go through each sob mesh for (int n=0; n(pGI); if (!pMI) continue; // to get the transform, the node has to be in the tree, so temporarily show if hidden //bool bShowing = sob.m_bShow; //sob.Show(true); Merge_MI(*pMI, verts, normals, inds); //sob.Show(bShowing); } assert (pMerged); //MeshInstance * pMI = memnew(MeshInstance); //pMI->set_name("Merged"); //add_child(pMI); //Ref rmesh = pMI->get_mesh(); LPRINT(5, "Merging, num verts is " + itos(verts.size())); // lightmap unwrap //LightmapUnwrap(verts, normals, inds, uv2s); // unmerge the original meshes (write the uvs2 back) Ref am; am.instance(); Array arr; arr.resize(Mesh::ARRAY_MAX); arr[Mesh::ARRAY_VERTEX] = verts; arr[Mesh::ARRAY_NORMAL] = normals; arr[Mesh::ARRAY_INDEX] = inds; // arr[Mesh::ARRAY_TEX_UV2] = uv2s; // bug fix. The lightmapping code removes duplicate vertices, which we DONT want // as it makes the merged mesh get out of sync with the original meshes. // To prevent this we will create a dummy set of UV1s that are unique. // PoolVector dummy_uvs; // for (int n=0; nadd_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, arr, Array(), Mesh::ARRAY_COMPRESS_DEFAULT); if (bLightmapUnwrap) LightmapUnwrap(am, pMerged->get_global_transform()); // duplicate the UV2 to uv1 just in case they are needed arr[Mesh::ARRAY_TEX_UV] = arr[Mesh::ARRAY_TEX_UV2]; pMerged->set_mesh(am); //DebugCountUVs(*pMerged); // check the num of uvs match the number of verts //int numUVs = DebugCountUVs(*pMerged); // set mesh to use in baked lighting pMerged->set_flag(GeometryInstance::FLAG_USE_BAKED_LIGHT, true); return true; // bake // Node * pBLN = pMerged->get_parent()->find_node("BakedLightmap"); // BakedLightmap * pBL = Object::cast_to(pBLN); // if (!pBL) // { // LWARN(5, "BakedLightmap not found"); // return false; // } // { // BakedLightmap::BakeError err; // err = pBL->bake(pMerged); // switch (err) // { // case BakedLightmap::BAKE_ERROR_NO_SAVE_PATH: // LWARN(5, "Can't determine a save path for lightmap images.\nSave your scene (for images to be saved in the same dir), or pick a save path from the BakedLightmap properties."); // break; // case BakedLightmap::BAKE_ERROR_NO_MESHES: // LWARN(5, "No meshes to bake. Make sure they contain an UV2 channel and that the 'Bake Light' flag is on."); // break; // case BakedLightmap::BAKE_ERROR_CANT_CREATE_IMAGE: // LWARN(5, "Failed creating lightmap images, make sure path is writable."); // break; // default: { // } // } // } // return true; } bool LHelper::LightmapUnwrap(Ref am, const Transform &trans) { // ArrayMesh // Ref rmesh = pMesh->get_mesh(); // we can add the UV2 coords from here Error err = am->lightmap_unwrap(trans); if (err != OK) { LWARN(5, "UV Unwrap failed, mesh may not be manifold?"); return false; } return true; } void LHelper::SetOwnerRecursive(Node * pNode, Node * pOwner) { pNode->set_owner(pOwner); for (int n=0; nget_child_count(); n++) { SetOwnerRecursive(pNode->get_child(n), pOwner); } } bool LHelper::BakeLightmap(BakedLightmap &baked_lightmap, MeshInstance * pMerged) { // bake BakedLightmap::BakeError err; // err = baked_lightmap.bake(pMerged); Node * pStartNode = baked_lightmap.get_parent(); // baked lightmap only picks up meshes and traverses if the owner is set.... (!) SetOwnerRecursive(pStartNode, pStartNode); err = baked_lightmap.bake(pStartNode); switch (err) { case BakedLightmap::BAKE_ERROR_NO_SAVE_PATH: { LWARN(5, "Can't determine a save path for lightmap images.\nSave your scene (for images to be saved in the same dir), or pick a save path from the BakedLightmap properties."); return false; } break; case BakedLightmap::BAKE_ERROR_NO_MESHES: { LWARN(5, "No meshes to bake. Make sure they contain an UV2 channel and that the 'Bake Light' flag is on."); return false; } break; case BakedLightmap::BAKE_ERROR_CANT_CREATE_IMAGE: { LWARN(5, "Failed creating lightmap images, make sure path is writable."); return false; } break; default: { } } return true; } void LHelper::Transform_Verts(const PoolVector &ptsLocal, PoolVector &ptsWorld, const Transform &tr) const { for (int n=0; n &normsLocal, PoolVector &normsWorld, const Transform &tr) const { for (int n=0; n &verts, PoolVector &norms, PoolVector &inds) { // some godot jiggery pokery to get the mesh verts in local space Ref rmesh = mi.get_mesh(); Array arrays = rmesh->surface_get_arrays(0); PoolVector p_vertices = arrays[VS::ARRAY_VERTEX]; PoolVector p_normals = arrays[VS::ARRAY_NORMAL]; PoolVector p_indices = arrays[VS::ARRAY_INDEX]; //PoolVector::Read ir = mesh_indices.read(); // the first index of this mesh is offset from the verts we already have stored in the merged mesh int first_index = verts.size(); // LPRINT(2, "Merge MI : " + mi.get_name() + "\tFirstVert : " + itos(first_index) + "\tNumUVs : " + itos(p_vertices.size())); // transform verts to world space Transform trans = mi.get_global_transform(); for (int n=0; n &p_verts, const PoolVector &p_normals, const PoolVector &p_inds, PoolVector &r_uvs) { ERR_FAIL_COND_V(!array_mesh_lightmap_unwrap_callback, ERR_UNCONFIGURED); assert (p_verts.size() == p_normals.size()); Vector verts; for (int n=0; n normals; for (int n=0; n inds; for (int n=0; n face_mats; for (int n=0; nwidth; // *r_size_hint_y = atlas->height; // float w = *r_size_hint_x; // float h = *r_size_hint_y; // if (w == 0 || h == 0) { // return false; //could not bake because there is no area // } // const xatlas::Mesh &output = atlas->meshes[0]; // *r_vertex = (int *)malloc(sizeof(int) * output.vertexCount); // *r_verts = (float *)malloc(sizeof(xatlas::Vertex) * output.vertexCount); // *r_index = (int *)malloc(sizeof(int) * output.indexCount); // 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("Final texture size: %f,%f - max %f,%f\n", w, h, max_x, max_y); // *r_vertex_count = output.vertexCount; // for (uint32_t i = 0; i < output.indexCount; i++) { // (*r_index)[i] = output.indexArray[i]; // } // *r_index_count = output.indexCount; // xatlas::Destroy(atlas); // printf("Done\n"); // return true; //} /* //Error ArrayMesh::lightmap_unwrap(const Transform &p_base_transform, float p_texel_size) { Error LHelper::lightmap_unwrap(ArrayMesh &am, const Transform &p_base_transform, float p_texel_size) { ERR_FAIL_COND_V(!array_mesh_lightmap_unwrap_callback, ERR_UNCONFIGURED); ERR_FAIL_COND_V_MSG(am.blend_shapes.size() != 0, ERR_UNAVAILABLE, "Can't unwrap mesh with blend shapes."); Vector vertices; Vector normals; Vector indices; Vector face_materials; Vector uv; Vector > uv_index; Vector surfaces; for (int i = 0; i < get_surface_count(); i++) { ArrayMeshLightmapSurface s; s.primitive = surface_get_primitive_type(i); ERR_FAIL_COND_V_MSG(s.primitive != Mesh::PRIMITIVE_TRIANGLES, ERR_UNAVAILABLE, "Only triangles are supported for lightmap unwrap."); s.format = surface_get_format(i); ERR_FAIL_COND_V_MSG(!(s.format & ARRAY_FORMAT_NORMAL), ERR_UNAVAILABLE, "Normals are required for lightmap unwrap."); Array arrays = surface_get_arrays(i); s.material = surface_get_material(i); s.vertices = SurfaceTool::create_vertex_array_from_triangle_arrays(arrays); PoolVector rvertices = arrays[Mesh::ARRAY_VERTEX]; int vc = rvertices.size(); PoolVector::Read r = rvertices.read(); PoolVector rnormals = arrays[Mesh::ARRAY_NORMAL]; PoolVector::Read rn = rnormals.read(); int vertex_ofs = vertices.size() / 3; vertices.resize((vertex_ofs + vc) * 3); normals.resize((vertex_ofs + vc) * 3); uv_index.resize(vertex_ofs + vc); for (int j = 0; j < vc; j++) { Vector3 v = p_base_transform.xform(r[j]); Vector3 n = p_base_transform.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_index.write[j + vertex_ofs] = Pair(i, j); } PoolVector rindices = arrays[Mesh::ARRAY_INDEX]; int ic = rindices.size(); 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()) continue; indices.push_back(vertex_ofs + j * 3 + 0); indices.push_back(vertex_ofs + j * 3 + 1); indices.push_back(vertex_ofs + j * 3 + 2); face_materials.push_back(i); } } else { PoolVector::Read ri = rindices.read(); 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()) 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]); face_materials.push_back(i); } } surfaces.push_back(s); } //unwrap float *gen_uvs; int *gen_vertices; int *gen_indices; int gen_vertex_count; int gen_index_count; 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(), face_materials.ptr(), indices.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 while (get_surface_count()) { surface_remove(0); } //create surfacetools for each surface.. Vector > surfaces_tools; for (int i = 0; i < surfaces.size(); i++) { Ref st; st.instance(); st->begin(Mesh::PRIMITIVE_TRIANGLES); st->set_material(surfaces[i].material); surfaces_tools.push_back(st); //stay there } 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_index.size(), ERR_BUG); ERR_FAIL_INDEX_V(gen_vertices[gen_indices[i + 1]], uv_index.size(), ERR_BUG); ERR_FAIL_INDEX_V(gen_vertices[gen_indices[i + 2]], uv_index.size(), ERR_BUG); ERR_FAIL_COND_V(uv_index[gen_vertices[gen_indices[i + 0]]].first != uv_index[gen_vertices[gen_indices[i + 1]]].first || uv_index[gen_vertices[gen_indices[i + 0]]].first != uv_index[gen_vertices[gen_indices[i + 2]]].first, ERR_BUG); int surface = uv_index[gen_vertices[gen_indices[i + 0]]].first; for (int j = 0; j < 3; j++) { SurfaceTool::Vertex v = surfaces[surface].vertices[uv_index[gen_vertices[gen_indices[i + j]]].second]; if (surfaces[surface].format & ARRAY_FORMAT_COLOR) { surfaces_tools.write[surface]->add_color(v.color); } if (surfaces[surface].format & ARRAY_FORMAT_TEX_UV) { surfaces_tools.write[surface]->add_uv(v.uv); } if (surfaces[surface].format & ARRAY_FORMAT_NORMAL) { surfaces_tools.write[surface]->add_normal(v.normal); } if (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]->add_tangent(t); } if (surfaces[surface].format & ARRAY_FORMAT_BONES) { surfaces_tools.write[surface]->add_bones(v.bones); } if (surfaces[surface].format & ARRAY_FORMAT_WEIGHTS) { surfaces_tools.write[surface]->add_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]->add_uv2(uv2); surfaces_tools.write[surface]->add_vertex(v.vertex); } } //free stuff ::free(gen_vertices); ::free(gen_indices); ::free(gen_uvs); //generate surfaces for (int i = 0; i < surfaces_tools.size(); i++) { surfaces_tools.write[i]->index(); surfaces_tools.write[i]->commit(Ref((ArrayMesh *)this), surfaces[i].format); } set_lightmap_size_hint(Size2(size_x, size_y)); return OK; } */