// // EasyMesh-Internal— Internal operations // // Copyright © 2012—2020 Sam Hocevar // © 2009—2015 Cédric Lecacheur // © 2009—2015 Benjamin “Touky” Huet // // Lol Engine is free software. It comes without any warranty, to // the extent permitted by applicable law. You can redistribute it // and/or modify it under the terms of the Do What the Fuck You Want // to Public License, Version 2, as published by the WTFPL Task Force. // See http://www.wtfpl.net/ for more details. // #include namespace lol { //----------------------------------------------------------------------------- void EasyMesh::AddVertex(vec3 const &coord) { m_vert.push(VertexData(coord, vec3(0.f, 1.f, 0.f), BD()->ColorA())); m_state = MeshRender::NeedConvert; } //----------------------------------------------------------------------------- void EasyMesh::AddDupVertex(int i) { m_vert << m_vert[i]; m_state = MeshRender::NeedConvert; } //----------------------------------------------------------------------------- void EasyMesh::AddLerpVertex(int i, int j, float alpha) { AddLerpVertex(m_vert[i], m_vert[j], alpha); } void EasyMesh::AddLerpVertex(VertexData const &vi, VertexData const &vj, float alpha) { m_vert.push(GetLerpVertex(vi, vj, alpha)); m_state = MeshRender::NeedConvert; } //----------------------------------------------------------------------------- VertexData EasyMesh::GetLerpVertex(int i, int j, float alpha) { return GetLerpVertex(m_vert[i], m_vert[j], alpha); } VertexData EasyMesh::GetLerpVertex(VertexData const &vi, VertexData const &vj, float alpha) { return VertexData( lol::lerp(vi.m_coord, vj.m_coord, alpha), lol::lerp(vi.m_normal, vj.m_normal, alpha), lol::lerp(vi.m_color, vj.m_color, alpha), lol::lerp(vi.m_texcoord, vj.m_texcoord, alpha), ((alpha < .5f) ? (vi.m_bone_id) : (vj.m_bone_id)), /* FIXME ? */ lol::lerp(vi.m_bone_weight, vj.m_bone_weight, alpha)); } //----------------------------------------------------------------------------- void EasyMesh::AddQuad(int i1, int i2, int i3, int i4, int base, bool duplicate) { if (duplicate) { int vbase = m_vert.count(); AddDupVertex(base + i1); AddDupVertex(base + i2); AddDupVertex(base + i3); AddDupVertex(base + i4); AddQuad(0, 1, 2, 3, vbase); } else { if (BD()->IsEnabled(MeshBuildOperation::QuadWeighting) && !BD()->IsEnabled(MeshBuildOperation::IgnoreQuadWeighting)) { int i5 = m_vert.count(); AddLerpVertex(GetLerpVertex(base + i1, base + i3, .5f), GetLerpVertex(base + i2, base + i4, .5f), .5f); m_indices << i1 + base; m_indices << i2 + base; m_indices << i5; m_indices << i2 + base; m_indices << i3 + base; m_indices << i5; m_indices << i4 + base; m_indices << i1 + base; m_indices << i5; m_indices << i5; m_indices << i3 + base; m_indices << i4 + base; } else { m_indices << i1 + base; m_indices << i2 + base; m_indices << i3 + base; m_indices << i4 + base; m_indices << i1 + base; m_indices << i3 + base; } } } //----------------------------------------------------------------------------- void EasyMesh::AddTriangle(int i1, int i2, int i3, int base, bool duplicate) { if (duplicate) { m_indices << (uint16_t)m_vert.count(); AddDupVertex(base + i1); m_indices << (uint16_t)m_vert.count(); AddDupVertex(base + i2); m_indices << (uint16_t)m_vert.count(); AddDupVertex(base + i3); } else { m_indices << base + i1; m_indices << base + i2; m_indices << base + i3; } } //----------------------------------------------------------------------------- void EasyMesh::ComputeNormals(int start, int vcount) { if (BD()->IsEnabled(MeshBuildOperation::CommandExecution) && BD()->IsEnabled(MeshBuildOperation::PostBuildComputeNormals)) return; easy_array< easy_array > normals; normals.resize(m_vert.count()); for (int i = 0; i < vcount; i += 3) { vec3 v0 = m_vert[m_indices[start + i + 2]].m_coord - m_vert[m_indices[start + i + 0]].m_coord; vec3 v1 = m_vert[m_indices[start + i + 1]].m_coord - m_vert[m_indices[start + i + 0]].m_coord; vec3 n = normalize(cross(v1, v0)); for (int j = 0; j < 3; j++) normals[m_indices[start + i + j]] << n; } for (int i = 0; i < normals.count(); i++) { if (normals[i].count() > 0) { //remove doubles for (int j = 0; j < normals[i].count(); ++j) for (int k = j + 1; k < normals[i].count(); ++k) if (1.f - dot(normals[i][k], normals[i][j]) < .00001f) normals[i].remove(k--); vec3 newv = vec3::zero; for (int j = 0; j < normals[i].count(); ++j) newv += normals[i][j]; m_vert[i].m_normal = normalize(newv / (float)normals[i].count()); } } } //----------------------------------------------------------------------------- void EasyMesh::VerticesCleanup() { easy_array vert_ids; vert_ids.resize(m_vert.count(), 0); //1: Remove triangles with two vertices on each other for (int i = 0; i < m_indices.count(); i += 3) { bool remove = false; for (int j = 0; !remove && j < 3; ++j) if (length(m_vert[m_indices[i + j]].m_coord - m_vert[m_indices[i + (j + 1) % 3]].m_coord) < .00001f) remove = true; if (remove) { m_indices.remove_swap(i, 3); i -= 3; } else { //1.5: Mark all used vertices for (int j = 0; j < 3; ++j) vert_ids[m_indices[i + j]] = 1; } } //2: Remove all unused vertices easy_array old_vert = m_vert; int shift = 0; m_vert.clear(); for (int i = 0; i < vert_ids.count(); ++i) { //Unused vertex, update the shift quantity instead of keeping it. if (vert_ids[i] == 0) shift++; else m_vert << old_vert[i]; //Always mark it with the shift quantity vert_ids[i] = shift; } //3: Update the indices for (int i = 0; i < m_indices.count(); ++i) m_indices[i] -= vert_ids[m_indices[i]]; } //----------------------------------------------------------------------------- void EasyMesh::VerticesMerge() { if (BD()->IsEnabled(MeshBuildOperation::CommandRecording)) { BD()->CmdStack().AddCmd(EasyMeshCmdType::VerticesMerge); return; } //1: Crunch all vertices in the dictionnary VertexDictionnary vert_dict; for (int i = std::get<0>(m_cursors.last()); i < m_vert.count(); i++) vert_dict.RegisterVertex(i, m_vert[i].m_coord); //2: Update the indices for (int i = 0; i < m_indices.count(); ++i) { int master = vert_dict.FindVertexMaster(m_indices[i]); if (master >= 0) m_indices[i] = master; } //2: Cleanup VerticesCleanup(); } //----------------------------------------------------------------------------- void EasyMesh::VerticesSeparate() { if (BD()->IsEnabled(MeshBuildOperation::CommandRecording)) { BD()->CmdStack().AddCmd(EasyMeshCmdType::VerticesSeparate); return; } easy_array< easy_array > new_ids; easy_array vert_ids; vert_ids.resize(m_vert.count(), 0); //1: Mark all used vertices for (int i = 0; i < m_indices.count(); ++i) vert_ids[m_indices[i]]++; //2: Update the vertices int vbase = std::get<0>(m_cursors.last()); int vcount = m_vert.count(); new_ids.resize(vcount); for (int i = vbase; i < vcount; i++) { while (vert_ids[i] > 1) { //Add duplicate new_ids[i] << m_vert.count(); AddDupVertex(i); vert_ids[i]--; } } //3: Update the indices for (int i = 0; i < m_indices.count(); ++i) { if (new_ids[m_indices[i]].count()) { int j = new_ids[m_indices[i]].pop(); m_indices[i] = j; } } //4: Cleanup VerticesCleanup(); } //----------------------------------------------------------------------------- void EasyMesh::ComputeTexCoord(float uv_scale, int uv_offset) { (void)uv_scale; (void)uv_offset; #if 0 VertexDictionnary vert_dict; easy_array tri_list; tri_list.Reserve(m_indices.count() - std::get<1>(m_cursors.last())); for (int i = std::get<1>(m_cursors.last()); i < m_indices.count(); i++) { vert_dict.RegisterVertex(m_indices[i], m_vert[m_indices[i]].m_coord); tri_list << m_indices[i]; } //full triangle count easy_array tri_done; easy_array tri_check; int tri_count = (m_indices.count() - std::get<1>(m_cursors.last())) / 3; tri_check << tri_list[0]; while (tri_check.count()) { int cur_tri = tri_check[0]; int v[3] = { tri_list[cur_tri + uv_offset % 3], tri_list[cur_tri + (1 + uv_offset) % 3], tri_list[cur_tri + (2 + uv_offset) % 3] }; vec2 uv[3] = { m_vert[tri_list[cur_tri]].m_texcoord.xy, m_vert[tri_list[cur_tri + 1]].m_texcoord.xy, m_vert[tri_list[cur_tri + 2]].m_texcoord.xy }; for (int j = 0; j < 3; j++) { if (uv[j] != vec2(-1.0f) && uv[j] == uv[(j + 1) % 3]) { uv[0] = vec2(-1.0f); uv[1] = vec2(-1.0f); uv[2] = vec2(-1.0f); break; } } int uv_set = 0; for (int j = 0; j < 3; j++) uv_set += (uv[j].x < 0.f)?(0):(1); //this case shouldn't happen. if (uv_set == 1) { /* for (int j = 0; j < 3; j++) { if (uv[j] != vec2(-1.0f)) { uv[(j + 1) % 2] = uv[j] + vec2(.0f, uv_scale * length(std::get<0>(m_vert[v[j]]) - std::get<0>(m_vert[v[(j + 1) % 3]]))); uv_set = 2; break; } } */ } //No UV is set, let's do the arbitrary set and use the basic method. if (uv_set == 0) { float new_dot = FLT_MAX; int base_i = 0; for (int j = 0; j < 3; j++) { float tmp_dot = abs(dot(normalize(m_vert[v[(j + 1) % 3]].m_coord - m_vert[v[j]].m_coord), normalize(m_vert[v[(j + 2) % 3]].m_coord - m_vert[v[j]].m_coord))); if (tmp_dot < new_dot) { base_i = j; new_dot = tmp_dot; } } uv[base_i] = vec2(.0f); uv[(base_i + 1) % 3] = vec2(.0f, uv_scale * length(m_vert[v[base_i]].m_coord - m_vert[v[(base_i + 1) % 3]].m_coord)); uv_set = 2; } //2 points have been set, let's figure the third if (uv_set == 2) { { //invert values so the two set uv are in [0, 1] slots. int new_v[3]; vec2 new_uv[3]; bool ignore_set = false; if (uv[0].x >= 0.f && uv[1].x < 0.f) { new_v[0] = v[2]; new_v[1] = v[0]; new_v[2] = v[1]; new_uv[0] = uv[2]; new_uv[1] = uv[0]; new_uv[2] = uv[1]; } else if (uv[0].x < 0.f && uv[1].x >= 0.f) { new_v[0] = v[1]; new_v[1] = v[2]; new_v[2] = v[0]; new_uv[0] = uv[1]; new_uv[1] = uv[2]; new_uv[2] = uv[0]; } else ignore_set = true; if (!ignore_set) { v[0] = new_v[0]; v[1] = new_v[1]; v[2] = new_v[2]; uv[0] = new_uv[0]; uv[1] = new_uv[1]; uv[2] = new_uv[2]; } } //Do this to be sure the normal is OK. ComputeNormals(cur_tri, 3); vec3 v01 = normalize(m_vert[v[1]].m_coord - m_vert[v[0]].m_coord); vec3 v02 = m_vert[v[2]].m_coord - m_vert[v[0]].m_coord; vec3 v_dir = normalize(cross(m_vert[m_indices[cur_tri]].m_normal, v01)); vec2 texu_dir = uv[1] - uv[0]; vec2 texv_dir = vec2(texu_dir.y, texu_dir.x); //Final calculations uv[2] = texu_dir * dot(v01, v02) + texv_dir * dot(v_dir, v02); //Set UV on ALL matching vertices! easy_array matching_vert; for (int i = 0; i < 3; i++) { #if 1 //This marks all same position UV to the same values //Deactivation is a test. matching_vert << v[i]; vert_dict.FindMatchingVertices(v[i], matching_vert); for (int j = 0; j < matching_vert.count(); j++) if (m_vert[matching_vert[j]].m_texcoord.xy == vec2(-1.0f)) m_vert[matching_vert[j]].m_texcoord = vec4(abs(uv[i]), m_vert[matching_vert[j]].m_texcoord.zw); #else m_vert[v[i]].m_texcoord = abs(uv[i]); #endif } tri_done << cur_tri; tri_check.remove(0); //Get connected triangles and go from there. for (int j = 0; j < 3; j++) { #if 1 //This finds triangle that are connected to this triangle vert_dict.FindConnectedTriangles(ivec2(v[j], v[(j + 1) % 3]), tri_list, tri_check, &tri_done); #else //This finds triangle that are connected to the vertices of this triangle vert_dict.FindConnectedTriangles(v[j], tri_list, tri_check, &tri_done); #endif } } else if (uv_set == 3) { for (int j = 0; j < 3; j++) { m_vert[tri_list[cur_tri]].m_texcoord = vec4(vec2(-1.0f), m_vert[tri_list[cur_tri]].m_texcoord.zw); m_vert[tri_list[cur_tri + 1]].m_texcoord = vec4(vec2(-1.0f), m_vert[tri_list[cur_tri + 1]].m_texcoord.zw); m_vert[tri_list[cur_tri + 2]].m_texcoord = vec4(vec2(-1.0f), m_vert[tri_list[cur_tri + 2]].m_texcoord.zw); } //uv[0] = vec2(-1.0f); //uv[1] = vec2(-1.0f); //uv[2] = vec2(-1.0f); /* bool tri_present = false; for (int j = 0; j < tri_done.count(); j++) if (cur_tri == tri_done[j]) tri_present = true; if (!tri_present) tri_done << cur_tri; tri_check.remove(0); */ } if (tri_check.count() == 0 && tri_done.count() != tri_count) { //look for unset triangle for (int i = 0; !tri_check.count() && i < tri_list.count(); i += 3) { bool tri_present = false; for (int j = 0; j < tri_done.count(); j++) if (i == tri_done[j]) tri_present = true; if (!tri_present) tri_check << i; } } } #endif } //----------------------------------------------------------------------------- void EasyMesh::SetTexCoordData(vec2 const &new_offset, vec2 const &new_scale) { BD()->TexCoordOffset() = new_offset; BD()->TexCoordScale() = new_scale; } //----------------------------------------------------------------------------- void EasyMesh::SetTexCoordData2(vec2 const &new_offset, vec2 const &new_scale) { BD()->TexCoordOffset2() = new_offset; BD()->TexCoordScale2() = new_scale; } //----------------------------------------------------------------------------- void EasyMesh::SetCurVertNormal(vec3 const &normal) { m_vert.last().m_normal = normal; } //----------------------------------------------------------------------------- void EasyMesh::SetCurVertColor(vec4 const &col) { m_vert.last().m_color = col; } //----------------------------------------------------------------------------- void EasyMesh::SetCurVertTexCoord(vec2 const &texcoord) { m_vert.last().m_texcoord = vec4(texcoord, m_vert.last().m_texcoord.zw); } //----------------------------------------------------------------------------- void EasyMesh::SetCurVertTexCoord2(vec2 const &texcoord) { m_vert.last().m_texcoord = vec4(m_vert.last().m_texcoord.xy, texcoord); } } /* namespace lol */