UNPKG

@woosh/meep-engine

Version:

Pure JavaScript game engine. Fully featured and production ready.

264 lines (205 loc) • 8.12 kB
import { array_copy_unique } from "../../../../collection/array/array_copy_unique.js"; import BinaryHeap from "../../../../collection/heap/BinaryHeap.js"; import { update_topo_face_normals } from "../update_topo_face_normals.js"; import { build_edge_collapse_candidates } from "./build_edge_collapse_candidates.js"; import { collapse_all_degenerate_edges } from "./collapse_all_degenerate_edges.js"; import { collapse_degenerate_edge } from "./collapse_degenerate_edge.js"; import { EdgeCollapseCandidate } from "./EdgeCollapseCandidate.js"; import { build_vertex_quadratics } from "./quadratic/build_vertex_quadratics.js"; const scratch_array_1 = []; const scratch_array_2 = []; /** * * @param {EdgeCollapseCandidate} edge */ export function extract_edge_cost(edge) { return edge.cost; } /** * @param {number} number_faces_to_remove minimum number of faces to removed * @param {BinaryHeap<EdgeCollapseCandidate>} heap * @param {TopoMesh} mesh * @param {Map<TopoEdge, EdgeCollapseCandidate>} edge_to_collapse_map * @param {Map<number, Quadratic3>} vertex_quadratics * @param {Set<number>} restricted_vertices */ export function collapse_edges( number_faces_to_remove, heap, mesh, edge_to_collapse_map, vertex_quadratics, restricted_vertices ) { const mesh_faces = mesh.getFaces(); const initial_face_count = mesh_faces.size; const target_face_count = initial_face_count - number_faces_to_remove; while (mesh_faces.size > target_face_count && !heap.isEmpty()) { const collapse_candidate = heap.pop(); if (!collapse_candidate.edge.isLinked()) { // no longer connected continue; } // if (!collapse_candidate.validate()) { // console.log('!'); // // // edge is out of date, update it // collapse_candidate.update(); // // // push back onto the heap // open_set.push(collapse_candidate); // continue; // } // find edges that need to be updated const victim_vertex = collapse_candidate.victim_vertex; // record edges who's cost that might have been affected by collapse const neighbour_count = victim_vertex.computeNeighbourVertices(scratch_array_2, 0); // debugValidateMesh(mesh); // perform edge collapse collapse_candidate.collapse(mesh, vertex_quadratics); // debugValidateMesh(mesh); const victim_vertex_attached_faces = victim_vertex.faces; // const victim_vertex_attached_faces = collapse_candidate.target_vertex.faces; const victim_vertex_attached_face_count = victim_vertex_attached_faces.length; // update face normals around collapsed vertex update_topo_face_normals(victim_vertex_attached_faces, victim_vertex_attached_face_count); let update_edge_count = 0; // for (let i = 0; i < victim_vertex_attached_face_count; i++) { // const victim_face = victim_vertex_attached_faces[i]; // // if (!victim_face.isLinked()) { // // disconnected face // continue; // } // // const edges = victim_face.edges; // // const addition_count = array_copy_unique(edges, 0, scratch_array_1, update_edge_count, edges.length); // // update_edge_count += addition_count; // } update_edge_count += array_copy_unique(collapse_candidate.target_vertex.edges, 0, scratch_array_1, update_edge_count, victim_vertex.edges.length); // const copy_of_update_edges = scratch_array_1.slice(0, update_edge_count); // for (let i = 0; i < neighbour_count; i++) { /** * * @type {TopoVertex} */ const v = scratch_array_2[i]; const edges = v.edges; const addition_count = array_copy_unique(edges, 0, scratch_array_1, update_edge_count, edges.length); update_edge_count += addition_count; } // update edge costs for (let i = 0; i < update_edge_count; i++) { /** * * @type {TopoEdge} */ const edge = scratch_array_1[i]; const edge_faces = edge.faces; const edge_face_count = edge_faces.length; if (edge_face_count <= 0) { //disconnected edge, skip continue; } const collapseEdge = edge_to_collapse_map.get(edge); const heap_index = heap.data.indexOf(collapseEdge); if (heap_index === -1) { // edge is not in the heap, meaning it was already processed continue; } if (edge.isDegenerateEdge()) { if (edge.isLinked()) { // edge is degenerate, it can be safely removed collapse_degenerate_edge(edge, mesh); } // removed_edges.push(collapseEdge); // exclude edge heap.deleteByIndex(heap_index); continue; } // update length edge.computeSquaredLength(); // recompute cost const is_valid = collapseEdge.update(vertex_quadratics, restricted_vertices); if (is_valid) { // update the heap, re-scoring element heap.bubbleDown(heap_index); } else { // edge is no longer valid for collapse, remove from heap heap.deleteByIndex(heap_index); } } // DEBUG validate cost // for (let i = 0; i < open_set.length; i++) { // const datum = open_set.data[i]; // // if (!datum.edge.isLinked()) { // continue; // } // // if (!datum.validate()) { // debugger; // } // // const delta = datum.validateUpdate(); // if (delta !== 0) { // debugger; // } // } } } /** * Simplifies a given topology mesh by reducing number of faces. Preserves original vertices * NOTE: preserves outline of the mesh, that is the open edge * NOTE: assumes that face normals are set * @param {TopoMesh} mesh * @param {number} num_faces_to_remove desired number of faces to remove * @param {Set<number>} [restricted_vertices] vertices that are not allowed to be removed */ export function simplifyTopoMesh(mesh, num_faces_to_remove, restricted_vertices = new Set()) { const faces = mesh.getFaces(); const face_count = faces.size; if (num_faces_to_remove <= 0 || face_count <= 0) { // we're already at the target return; } /** * * @type {BinaryHeap<EdgeCollapseCandidate>} */ const open_set = new BinaryHeap(extract_edge_cost); /** * * @type {Map<TopoEdge, EdgeCollapseCandidate>} */ const edge_to_collapse_map = new Map(); /** * * @type {Map<number, Quadratic3>} */ const vertex_quadratics = new Map(); build_vertex_quadratics({ mesh: mesh, quadratics: vertex_quadratics }); // compute collapse cost of each vertex/edge pair build_edge_collapse_candidates( mesh, open_set, edge_to_collapse_map, vertex_quadratics, restricted_vertices ); collapse_edges( num_faces_to_remove, open_set, mesh, edge_to_collapse_map, vertex_quadratics, restricted_vertices ); // debugValidateMesh(mesh); // get rid of degenerate edges collapse_all_degenerate_edges(mesh.getEdges(), mesh); // debugValidateMesh(mesh); }