// // EasyMesh-Csg: The code belonging to CSG operations // // Copyright © 2010—2019 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::CsgUnion() { MeshCsg(CSGUsage::Union); } void EasyMesh::CsgSub() { MeshCsg(CSGUsage::Substract); } void EasyMesh::CsgSubL() { MeshCsg(CSGUsage::SubstractLoss); } void EasyMesh::CsgAnd() { MeshCsg(CSGUsage::And); } void EasyMesh::CsgXor() { MeshCsg(CSGUsage::Xor); } //----------------------------------------------------------------------------- void EasyMesh::MeshCsg(CSGUsage csg_operation) { if (BD()->IsEnabled(MeshBuildOperation::CommandRecording)) { BD()->CmdStack().AddCmd(EasyMeshCmdType::MeshCsg); BD()->CmdStack() << csg_operation; return; } //A vertex dictionnary for vertices on the same spot. array< int, int > vertex_dict; //This list keeps track of the triangle that will need deletion at the end. array< int > triangle_to_kill; //Listing for each triangle of the vectors intersecting it. > array< int, array< vec3, vec3, vec3 > > triangle_isec; //keep a track of the intersection point on the triangle. array< vec3, int > triangle_vertex; for (int k = 0; k < 10; k++) triangle_vertex.push(vec3(.0f), 0); //bsp infos CsgBsp mesh_bsp_0; CsgBsp mesh_bsp_1; if (m_cursors.count() == 0) return; //BSP BUILD : We use the brace logic, csg should be used as : "[ exp .... [exp .... csg]]" int cursor_start = (m_cursors.count() < 2)?(0):(m_cursors[(m_cursors.count() - 2)].m2); for (int mesh_id = 0; mesh_id < 2; mesh_id++) { int start_point = (mesh_id == 0) ? (cursor_start) : (m_cursors.last().m2); int end_point = (mesh_id == 0) ? (m_cursors.last().m2) : (m_indices.count()); CsgBsp &mesh_bsp = (mesh_id == 0) ? (mesh_bsp_0) : (mesh_bsp_1); for (int i = start_point; i < end_point; i += 3) mesh_bsp.AddTriangleToTree(i, m_vert[m_indices[i]].m_coord, m_vert[m_indices[i + 1]].m_coord, m_vert[m_indices[i + 2]].m_coord); } //BSP Usage : let's crunch all triangles on the correct BSP int indices_count = m_indices.count(); for (int mesh_id = 0; mesh_id < 2; mesh_id++) { int start_point = (mesh_id == 0) ? (cursor_start) : (m_cursors.last().m2); int end_point = (mesh_id == 0) ? (m_cursors.last().m2) : (indices_count); CsgBsp &mesh_bsp = (mesh_id == 0) ? (mesh_bsp_1) : (mesh_bsp_0); array< vec3, int, int, float > vert_list; array< int, int, int, int > tri_list; vec3 n0(.0f); vec3 n1(.0f); vec4 c0(.0f); vec4 c1(.0f); //Reserve some memory vert_list.reserve(3); tri_list.reserve(3); for (int i = start_point; i < end_point; i += 3) { int Result = mesh_bsp.TestTriangleToTree(m_vert[m_indices[i]].m_coord, m_vert[m_indices[i + 1]].m_coord, m_vert[m_indices[i + 2]].m_coord, vert_list, tri_list); int tri_base_idx = m_indices.count(); //one split has been done, we need to had the new vertices & the new triangles. if (Result == 1) { triangle_to_kill.push(i); #if 1 int base_idx = m_vert.count(); for (int k = 3; k < vert_list.count(); k++) { int P0 = (vert_list[k].m2 < 3) ? (m_indices[i + vert_list[k].m2]) : (base_idx + vert_list[k].m2 - 3); int P1 = (vert_list[k].m3 < 3) ? (m_indices[i + vert_list[k].m3]) : (base_idx + vert_list[k].m3 - 3); AddVertex(vert_list[k].m1); //Normal : bad calculations there. n0 = m_vert[P0].m_normal; n1 = m_vert[P1].m_normal; SetCurVertNormal(normalize(n0 + (n1 - n0) * vert_list[k].m4)); #if 1 //Color c0 = m_vert[P0].m_color; c1 = m_vert[P1].m_color; vec4 res = c0 + ((c1 - c0) * vert_list[k].m4); SetCurVertColor(res); #else if (mesh_id == 0) SetCurVertColor(vec4(1.0f, .0f, .0f, 1.0f)); else SetCurVertColor(vec4(.0f, 1.0f, 1.0f, 1.0f)); #endif } for (int k = 0; k < tri_list.count(); k++) { int P0 = (tri_list[k].m2 < 3) ? (m_indices[i + tri_list[k].m2]) : (base_idx + (tri_list[k].m2 - 3)); int P1 = (tri_list[k].m3 < 3) ? (m_indices[i + tri_list[k].m3]) : (base_idx + (tri_list[k].m3 - 3)); int P2 = (tri_list[k].m4 < 3) ? (m_indices[i + tri_list[k].m4]) : (base_idx + (tri_list[k].m4 - 3)); AddTriangle(P0, P1, P2, 0); } #endif } #if 1 //Main case if (Result >= 0) { for (int k = 0; k < tri_list.count(); k++) { int tri_idx = (tri_list.count() == 1) ? (i) : (tri_base_idx + k * 3); //Triangle Kill Test if (//csgu : CSGUnion() -> m0_Outside + m1_Outside (csg_operation == CSGUsage::Union && tri_list[k].m1 == LEAF_BACK) || //csgs : CsgSub() -> m0_Outside + m1_Inside-inverted (csg_operation == CSGUsage::Substract && ((mesh_id == 0 && tri_list[k].m1 == LEAF_BACK) || (mesh_id == 1 && tri_list[k].m1 == LEAF_FRONT))) || //csgs : CsgSubL() -> m0_Outside (csg_operation == CSGUsage::SubstractLoss && ((mesh_id == 0 && tri_list[k].m1 == LEAF_BACK) || mesh_id == 1)) || //csga : CSGAnd() -> m0_Inside + m1_Inside (csg_operation == CSGUsage::And && tri_list[k].m1 == LEAF_FRONT)) { triangle_to_kill.push(tri_idx); } //Triangle Invert Test if (//csgs : CsgSub() -> m0_Outside + m1_Inside-inverted (csg_operation == CSGUsage::Substract && mesh_id == 1 && tri_list[k].m1 == LEAF_BACK) || //csgx : CSGXor() -> m0_Outside/m0_Inside-inverted + m1_Outside/m1_Inside-inverted (csg_operation == CSGUsage::Xor && tri_list[k].m1 == LEAF_BACK)) { //a Xor means we will share vertices with the outside, so duplicate the vertices. //TODO : This operation disconnect all triangle, in some cases, not a good thing. if (csg_operation == CSGUsage::Xor) { for (int l = 0; l < 3; l++) { AddDupVertex(m_indices[tri_idx + l]); m_indices[tri_idx + l] = (uint16_t)m_vert.count() - 1; } } m_indices[tri_idx + 1] += m_indices[tri_idx + 2]; m_indices[tri_idx + 2] = m_indices[tri_idx + 1] - m_indices[tri_idx + 2]; m_indices[tri_idx + 1] = m_indices[tri_idx + 1] - m_indices[tri_idx + 2]; ComputeNormals(tri_idx, 3); } } } #endif vert_list.clear(); tri_list.clear(); } } // FIXME: what was this code for? // for (int i = 0; i < m_vert.count(); i++) // if (length(m_vert[i].m_normal) < 1.0f) // i = i; int dir = 1; for (int i = 0; i >= 0 && i < triangle_to_kill.count() - 1; i += dir) { if (triangle_to_kill[i] < triangle_to_kill[i + 1] && dir < 0) dir = 1; if (triangle_to_kill[i] == triangle_to_kill[i + 1]) { triangle_to_kill.remove(i); dir = -1; } if (triangle_to_kill[i] > triangle_to_kill[i + 1]) { triangle_to_kill[i] += triangle_to_kill[i + 1]; triangle_to_kill[i + 1] = triangle_to_kill[i] - triangle_to_kill[i + 1]; triangle_to_kill[i] = triangle_to_kill[i] - triangle_to_kill[i + 1]; dir = -1; } if (i == 0 && dir == -1) dir = 1; } for (int i = triangle_to_kill.count() - 1; i >= 0; i--) m_indices.remove(triangle_to_kill[i], 3); m_cursors.last().m1 = m_vert.count(); m_cursors.last().m2 = m_indices.count(); VerticesCleanup(); //DONE for the splitting ! } } /* namespace lol */