From be95d123071cba5f6b0af544f059969a9680c10d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=91=E9=A3=8E?= Date: Sun, 31 May 2020 01:49:06 +0800 Subject: [PATCH] Add examples 42 : BunnyLOD (#2155) --- examples/42-bunnylod/bunnylod.cpp | 382 +++++++++++++++++++++ examples/42-bunnylod/progmesh.c | 529 ++++++++++++++++++++++++++++++ scripts/genie.lua | 1 + 3 files changed, 912 insertions(+) create mode 100644 examples/42-bunnylod/bunnylod.cpp create mode 100644 examples/42-bunnylod/progmesh.c diff --git a/examples/42-bunnylod/bunnylod.cpp b/examples/42-bunnylod/bunnylod.cpp new file mode 100644 index 000000000..f91d3ed7d --- /dev/null +++ b/examples/42-bunnylod/bunnylod.cpp @@ -0,0 +1,382 @@ +/* + * Copyright 2011-2020 Branimir Karadzic. All rights reserved. + * License: https://github.com/bkaradzic/bgfx#license-bsd-2-clause + */ + +#include "common.h" +#include "bgfx_utils.h" +#include "imgui/imgui.h" + +extern "C" void ProgressiveMesh(int vert_n, int vert_stride, const float *v, int tri_n, const int *tri, int *map, int *permutation); + +static void * Alloc(size_t sz) { + return BX_ALLOC(entry::getAllocator(), sz); +} + +static void Free(void *p) { + BX_FREE(entry::getAllocator(), p); +} + +namespace +{ + +class ExampleBunnyLOD : public entry::AppI +{ +public: + ExampleBunnyLOD(const char* _name, const char* _description, const char* _url) + : entry::AppI(_name, _description, _url) + { + } + + void PermuteMesh(const bgfx::Memory *vb, const bgfx::Memory *ib, const bgfx::VertexLayout &layout) { + int i; + int stride = layout.getStride(); + int offset = layout.getOffset(bgfx::Attrib::Position); + int vertices = vb->size / stride; + int triangles = ib->size / ( 3 * sizeof(uint32_t) ); + int *permutation = (int*)Alloc(vertices * sizeof(int)); + m_map = (int *)Alloc(vertices * sizeof(int)); + + // It will takes long time if there are too many vertices. + ProgressiveMesh(vertices, stride, (const float *)(vb->data + offset), triangles, (const int *)ib->data, m_map, permutation); + + // rearrange the vertex Array + char * temp = (char *)Alloc(vertices * stride); + bx::memCopy(temp, vb->data, vb->size); + for (i = 0; idata + permutation[i] * stride , temp + i * stride, stride); + } + Free(temp); + + // update the changes in the entries in the triangle Array + for (i = 0; idata + i * sizeof(uint32_t)); + *indices = permutation[*indices]; + } + + Free(permutation); + } + + int findDuplicateVertices(const char *vb, int n, const bgfx::VertexLayout &layout, int *map) { + int i,j; + int stride = layout.getStride(); + int poffset = layout.getOffset(bgfx::Attrib::Position); + for (i=0;idata + target*stride, vb + i*stride, stride); + ++target; + } + } + return buffer; + } + + void loadMesh(Mesh *mesh) { + // merge sub mesh + int vertices = 0; + int indices = 0; + for (GroupArray::const_iterator it = mesh->m_groups.begin(), itEnd = mesh->m_groups.end(); it != itEnd; ++it) { + vertices += it->m_numVertices; + indices += it->m_numIndices; + } + + const bgfx::Memory *ib = bgfx::alloc(indices * sizeof(uint32_t)); + char * vb_data = (char *)Alloc(mesh->m_layout.getSize(vertices)); + + size_t voffset = 0; + size_t ioffset = 0; + int index = 0; + for (GroupArray::const_iterator it = mesh->m_groups.begin(), itEnd = mesh->m_groups.end(); it != itEnd; ++it) { + size_t vsize = mesh->m_layout.getSize(it->m_numVertices); + bx::memCopy(vb_data + voffset, it->m_vertices, vsize); + uint32_t *ibptr = (uint32_t *)(ib->data + ioffset); + for (uint32_t i = 0; im_numIndices; i++) { + ibptr[i] = it->m_indices[i] + index; + } + voffset+=vsize; + ioffset+=it->m_numIndices * sizeof(uint32_t); + index+=it->m_numVertices; + } + + int * map = (int *)Alloc(vertices * sizeof(int)); + int merged = findDuplicateVertices(vb_data, vertices, mesh->m_layout, map); + + const bgfx::Memory *vb = mergeVertices(vb_data, mesh->m_layout.getStride(), map, vertices, merged); + Free(vb_data); + vertices = merged; + + int i; + int *ib_data = (int *)ib->data; + for (i=0; im_layout); + + m_triangle = (int *)Alloc(ib->size); + bx::memCopy(m_triangle, ib->data, ib->size); + + m_vb = bgfx::createVertexBuffer(vb, mesh->m_layout); + m_ib = bgfx::createDynamicIndexBuffer(ib, BGFX_BUFFER_INDEX32); + + m_numVertices = vertices; + m_numTriangles = indices/3; + m_totalVertices = m_numVertices; + m_totalTriangles = m_numTriangles; + } + + void init(int32_t _argc, const char* const* _argv, uint32_t _width, uint32_t _height) override + { + Args args(_argc, _argv); + + m_width = _width; + m_height = _height; + m_debug = BGFX_DEBUG_NONE; + m_reset = BGFX_RESET_VSYNC; + + bgfx::Init init; + init.type = args.m_type; + init.vendorId = args.m_pciId; + init.resolution.width = m_width; + init.resolution.height = m_height; + init.resolution.reset = m_reset; + bgfx::init(init); + + // Enable debug text. + bgfx::setDebug(m_debug); + + // Set view 0 clear state. + bgfx::setViewClear(0 + , BGFX_CLEAR_COLOR|BGFX_CLEAR_DEPTH + , 0x303030ff + , 1.0f + , 0 + ); + + u_tint = bgfx::createUniform("u_tint", bgfx::UniformType::Vec4); + + // Create program from shaders. + m_program = loadProgram("vs_picking_shaded", "fs_picking_shaded"); + + Mesh *mesh = meshLoad("meshes/bunny.bin", true); // load into memory + loadMesh(mesh); + meshUnload(mesh); + + m_timeOffset = bx::getHPCounter(); + m_LOD = 1.0f; + m_lastLOD = m_LOD; + + imguiCreate(); + } + + int shutdown() override + { + imguiDestroy(); + + // Cleanup. + bgfx::destroy(m_program); + bgfx::destroy(m_vb); + bgfx::destroy(m_ib); + bgfx::destroy(u_tint); + + Free(m_map); + Free(m_triangle); + + // Shutdown bgfx. + bgfx::shutdown(); + + return 0; + } + + void updateIndexBuffer() { + int verts = m_LOD * m_totalVertices; + if (verts <= 0) + return; + + int i,j; + int tris = 0; + const bgfx::Memory * ib = bgfx::alloc(m_totalTriangles * 3 * sizeof(uint32_t)); + + for (i = 0; i < (int)m_totalTriangles; i++) { + int v[3]; + for (j=0;j<3;j++) { + int idx = m_triangle[i*3+j]; + while (idx >= verts) { + idx = m_map[idx]; + } + v[j] = idx; + } + if (v[0] != v[1] && v[0] != v[2] && v[1] != v[2]) { + bx::memCopy(ib->data + tris * 3 * sizeof(uint32_t), v, 3 * sizeof(int)); + ++tris; + } + } + m_numTriangles = tris; + m_numVertices = verts; + + bgfx::update(m_ib, 0, ib); + + } + + void submitLOD(bgfx::ViewId viewid, const float *mtx) { + bgfx::setTransform(mtx); + bgfx::setState(0 + | BGFX_STATE_WRITE_RGB + | BGFX_STATE_WRITE_A + | BGFX_STATE_WRITE_Z + | BGFX_STATE_DEPTH_TEST_LESS + | BGFX_STATE_CULL_CCW + | BGFX_STATE_MSAA + ); + + if (m_LOD != m_lastLOD) { + updateIndexBuffer(); + m_lastLOD = m_LOD; + } + + bgfx::setIndexBuffer(m_ib, 0, m_numTriangles*3); + bgfx::setVertexBuffer(0, m_vb, 0, m_numVertices); + bgfx::submit(viewid, m_program); + } + + bool update() override + { + if (!entry::processEvents(m_width, m_height, m_debug, m_reset, &m_mouseState) ) + { + imguiBeginFrame(m_mouseState.m_mx + , m_mouseState.m_my + , (m_mouseState.m_buttons[entry::MouseButton::Left ] ? IMGUI_MBUT_LEFT : 0) + | (m_mouseState.m_buttons[entry::MouseButton::Right ] ? IMGUI_MBUT_RIGHT : 0) + | (m_mouseState.m_buttons[entry::MouseButton::Middle] ? IMGUI_MBUT_MIDDLE : 0) + , m_mouseState.m_mz + , uint16_t(m_width) + , uint16_t(m_height) + ); + + showExampleDialog(this); + + ImGui::SetNextWindowPos( + ImVec2(m_width - m_width / 5.0f - 10.0f, 10.0f) + , ImGuiCond_FirstUseEver + ); + ImGui::SetNextWindowSize( + ImVec2(m_width / 5.0f, m_height / 2.0f) + , ImGuiCond_FirstUseEver + ); + ImGui::Begin("Settings" + , NULL + , 0 + ); + + ImGui::Text("Vertices: %d", m_numVertices); + ImGui::Text("Triangles: %d", m_numTriangles); + ImGui::SliderFloat("LOD Level", &m_LOD, 0.0f, 1.0f); + + ImGui::End(); + + imguiEndFrame(); + + // Set view 0 default viewport. + bgfx::setViewRect(0, 0, 0, uint16_t(m_width), uint16_t(m_height) ); + + // This dummy draw call is here to make sure that view 0 is cleared + // if no other draw calls are submitted to view 0. + bgfx::touch(0); + + float time = (float)( (bx::getHPCounter()-m_timeOffset)/double(bx::getHPFrequency() ) ); + const float BasicColor[4] = { 1.0f, 1.0f, 1.0f, 1.0f }; + bgfx::setUniform(u_tint, BasicColor); + + const bx::Vec3 at = { 0.0f, 1.0f, 0.0f }; + const bx::Vec3 eye = { 0.0f, 1.0f, -2.5f }; + + // Set view and projection matrix for view 0. + { + float view[16]; + bx::mtxLookAt(view, eye, at); + + float proj[16]; + bx::mtxProj(proj, 60.0f, float(m_width)/float(m_height), 0.1f, 100.0f, bgfx::getCaps()->homogeneousDepth); + bgfx::setViewTransform(0, view, proj); + + // Set view 0 default viewport. + bgfx::setViewRect(0, 0, 0, uint16_t(m_width), uint16_t(m_height) ); + } + + float mtx[16]; + bx::mtxRotateXY(mtx + , 0.0f + , time*0.37f + ); + + submitLOD(0, mtx); + + // Advance to next frame. Rendering thread will be kicked to + // process submitted rendering primitives. + bgfx::frame(); + + return true; + } + + return false; + } + + entry::MouseState m_mouseState; + + uint32_t m_width; + uint32_t m_height; + uint32_t m_debug; + uint32_t m_reset; + + float m_lastLOD; + float m_LOD; + uint32_t m_numVertices; + uint32_t m_numTriangles; + uint32_t m_totalVertices; + uint32_t m_totalTriangles; + + int *m_map; + int *m_triangle; + + int64_t m_timeOffset; + bgfx::VertexBufferHandle m_vb; + bgfx::DynamicIndexBufferHandle m_ib; + bgfx::ProgramHandle m_program; + bgfx::UniformHandle u_tint; +}; + +} // namespace + +ENTRY_IMPLEMENT_MAIN( + ExampleBunnyLOD + , "42-bunnylod" + , "Bunny LOD" + , "https://bkaradzic.github.io/bgfx/examples.html#bunnylod" + ); diff --git a/examples/42-bunnylod/progmesh.c b/examples/42-bunnylod/progmesh.c new file mode 100644 index 000000000..4e970ba62 --- /dev/null +++ b/examples/42-bunnylod/progmesh.c @@ -0,0 +1,529 @@ +/* + * Progressive Mesh type Polygon Reduction Algorithm + * + * Original version by Stan Melax (c) 1998 + * C version by Cloud Wu (c) 2020 + * + * The function ProgressiveMesh() takes a model in an "indexed face + * set" sort of way. i.e. Array of vertices and Array of triangles. + * The function then does the polygon reduction algorithm + * internally and reduces the model all the way down to 0 + * vertices and then returns the order in which the + * vertices are collapsed and to which neighbor each vertex + * is collapsed to. More specifically the returned "permutation" + * indicates how to reorder your vertices so you can render + * an object by using the first n vertices (for the n + * vertex version). After permuting your vertices, the + * map Array indicates to which vertex each vertex is collapsed to. + */ + +#include +#include +#include + +#define ARRAY_SIZE 16 + +struct triangle { + int vertex[3]; // the 3 points (id) that make this tri + float normal[3]; // unit vector othogonal to this face +}; + +struct array { + int n; + int cap; + int *buffer; + int tmp[ARRAY_SIZE]; +}; + +struct vertex { + float position[3]; // location of point in euclidean space + int id; // place of vertex in original Array + struct array neighbor; // adjacent vertices + struct array face; // adjacent triangles + float objdist; // cached cost of collapsing edge + int collapse; // candidate vertex (id) for collapse +}; + +struct mesh { + int n_face; + int n_vertex; + struct vertex *v; + struct triangle *t; +}; + +// vec3 math + +static inline void +vec3_sub(const float v0[3], const float v1[3], float v[3]) { + v[0] = v0[0] - v1[0]; + v[1] = v0[1] - v1[1]; + v[2] = v0[2] - v1[2]; +} + +static inline void +vec3_cross(const float a[3], const float b[3], float v[3]) { + v[0] = a[1]*b[2] - a[2]*b[1]; + v[1] = a[2]*b[0] - a[0]*b[2]; + v[2] = a[0]*b[1] - a[1]*b[0]; +} + +static inline float +vec3_dot(const float a[3], const float b[3]) { + return a[0]*b[0] + a[1]*b[1] + a[2] * b[2]; +} + +static inline float +vec3_length(const float v[3]) { + return sqrtf(vec3_dot(v,v)); +} + +static inline void +vec3_normalize(float v[3]) { + const float invLen = 1.0f/vec3_length(v); + v[0] *= invLen; + v[1] *= invLen; + v[2] *= invLen; +} + +// array + +static void +array_init(struct array *a) { + a->n = 0; + a->cap = ARRAY_SIZE; + a->buffer = a->tmp; +} + +static void +array_deinit(struct array *a) { + if (a->buffer != a->tmp) { + free(a->buffer); + a->buffer = a->tmp; + a->cap = ARRAY_SIZE; + a->n = 0; + } +} + +static inline int +array_index(struct array *a, int idx) { + return a->buffer[idx]; +} + +static void +array_push(struct array *a, int v) { + if (a->n >= a->cap) { + int *old = a->buffer; + a->buffer = (int *)malloc(a->cap * 2 * sizeof(int)); + int i; + for (i=0;in;i++) { + a->buffer[i] = old[i]; + } + if (old != a->tmp) + free(old); + } + a->buffer[a->n++] = v; +} + +static inline void +array_remove_index(struct array *a, int idx) { + a->buffer[idx] = a->buffer[--a->n]; +} + +static void +array_remove(struct array *a, int v) { + int i; + for (i=0; in; i++) { + if (a->buffer[i] == v) { + array_remove_index(a, i); + return; + } + } +} + +static inline struct vertex * +Vertex(struct mesh *M, int id) { + return &M->v[id]; +} + +static inline struct triangle * +Triangle(struct mesh *M, int id) { + return &M->t[id]; +} + +static inline struct triangle * +Face(struct mesh *M, struct vertex *v, int idx) { + return Triangle(M, array_index(&v->face, idx)); +} + +static void +AddVertex(struct mesh *M, const float v[3]) { + int id = M->n_vertex++; + struct vertex * tmp = Vertex(M, id); + tmp->position[0] = v[0]; + tmp->position[1] = v[1]; + tmp->position[2] = v[2]; + tmp->id = id; + array_init(&tmp->neighbor); + array_init(&tmp->face); + tmp->objdist = 0; + tmp->collapse = -1; +} + +static void +RemoveVertex(struct mesh *M, int id) { + struct vertex * v = Vertex(M, id); + assert(v->id == id); + assert(v->face.n == 0); + int i; + for (i=0;iface.n;i++) { + struct vertex * nv = Vertex(M, array_index(&v->face, i)); + array_remove(&nv->neighbor, id); + } + v->id = -1; // invalid vertex id + array_deinit(&v->neighbor); + array_deinit(&v->face); +} + +static void +ComputeNormal(struct mesh *M, struct triangle *t) { + struct vertex * v0 = Vertex(M, t->vertex[0]); + struct vertex * v1 = Vertex(M, t->vertex[1]); + struct vertex * v2 = Vertex(M, t->vertex[2]); + float a[3], b[3]; + vec3_sub(v1->position, v0->position, a); + vec3_sub(v2->position, v1->position, b); + vec3_cross(a,b, t->normal); + vec3_normalize(t->normal); +} + +static void +AddNeighbor(struct mesh *M, int vid, int id) { + struct vertex *v = Vertex(M, vid); + int i; + for (i=0;ineighbor.n;i++) { + if (array_index(&v->neighbor,i) == id) + return; + } + array_push(&v->neighbor, id); +} + +#include + +static void +AddTriangle(struct mesh *M, const int v[3]) { + int v0 = v[0]; + int v1 = v[1]; + int v2 = v[2]; + if (v0 == v1 || v0 == v2 || v1 == v2) + return; + assert(v0 < M->n_vertex); + assert(v1 < M->n_vertex); + assert(v2 < M->n_vertex); + int id = M->n_face++; + struct triangle * tmp = Triangle(M, id); + tmp->vertex[0] = v0; + tmp->vertex[1] = v1; + tmp->vertex[2] = v2; + ComputeNormal(M, tmp); + + int i; + for(i=0;i<3;i++) { + struct vertex *obj = Vertex(M, v[i]); + array_push(&obj->face, id); + } + + AddNeighbor(M, v0, v1); + AddNeighbor(M, v0, v2); + AddNeighbor(M, v1, v0); + AddNeighbor(M, v1, v2); + AddNeighbor(M, v2, v0); + AddNeighbor(M, v2, v1); +} + +static int +HasVertex(struct triangle * t, int vid) { + return (t->vertex[0] == vid || t->vertex[1] == vid || t->vertex[2] == vid); +} + +static void +RemoveIfNonNeighbor_(struct mesh *M, struct vertex *v, int id) { + int i,j; + for (i=0;ineighbor.n;i++) { + if (array_index(&v->neighbor, i) == id) { + for (j=0;jface.n;j++) { + if (HasVertex(Face(M, v, j), id)) + return; + } + // remove from neighbors + array_remove_index(&v->neighbor, i); + return; + } + } +} + +static void +RemoveIfNonNeighbor(struct mesh *M, struct vertex *v0, struct vertex *v1) { + if (v0 == NULL || v1 == NULL) + return; + RemoveIfNonNeighbor_(M, v0, v1->id); + RemoveIfNonNeighbor_(M, v1, v0->id); +} + +static void +RemoveTriangle(struct mesh *M, int id) { + struct triangle * face = Triangle(M, id); + struct vertex * v[3]; + int i; + for (i=0;i<3;i++) { + v[i] = Vertex(M, face->vertex[i]); + if (v[i]->id < 0) + v[i] = NULL; + else { + array_remove(&v[i]->face, id); + } + } + RemoveIfNonNeighbor(M, v[0], v[1]); + RemoveIfNonNeighbor(M, v[1], v[2]); + RemoveIfNonNeighbor(M, v[2], v[0]); +} + +static void +ReplaceVertex(struct mesh *M, int faceid, int oldid, int newid) { + struct triangle * face = Triangle(M, faceid); + assert(oldid >=0 && newid >= 0); + assert(HasVertex(face, oldid)); + assert(!HasVertex(face, newid)); + if(oldid==face->vertex[0]){ + face->vertex[0]=newid; + } else if(oldid==face->vertex[1]){ + face->vertex[1]=newid; + } else { + face->vertex[2]=newid; + } + struct vertex *vold = Vertex(M, oldid); + struct vertex *vnew = Vertex(M, newid); + + array_remove(&vold->face, faceid); + array_push(&vnew->face, faceid); + + int i; + for (i = 0; i<3; i++) { + struct vertex *v = Vertex(M, face->vertex[i]); + RemoveIfNonNeighbor(M, vold, v); + } + + AddNeighbor(M, face->vertex[0], face->vertex[1]); + AddNeighbor(M, face->vertex[0], face->vertex[2]); + AddNeighbor(M, face->vertex[1], face->vertex[0]); + AddNeighbor(M, face->vertex[1], face->vertex[2]); + AddNeighbor(M, face->vertex[2], face->vertex[0]); + AddNeighbor(M, face->vertex[2], face->vertex[1]); + + ComputeNormal(M, face); +} + +static void +mesh_init(struct mesh *M, int vert_n, int tri_n) { + M->n_face = 0; + M->n_vertex = 0; + M->v = (struct vertex *)malloc(vert_n * sizeof(struct vertex)); + M->t = (struct triangle *)malloc(tri_n * sizeof(struct triangle)); +} + +static void +mesh_deinit(struct mesh *M) { + free(M->v); + free(M->t); +} + +static float +ComputeEdgeCollapseCost(struct mesh *M, struct vertex *u, int vid) { + // if we collapse edge uv by moving u to v then how + // much different will the model change, i.e. how much "error". + // Texture, vertex normal, and border vertex code was removed + // to keep this demo as simple as possible. + // The method of determining cost was designed in order + // to exploit small and coplanar regions for + // effective polygon reduction. + // Is is possible to add some checks here to see if "folds" + // would be generated. i.e. normal of a remaining face gets + // flipped. I never seemed to run into this problem and + // therefore never added code to detect this case. + struct vertex *v = Vertex(M, vid); + float tmp[3]; + vec3_sub(v->position, u->position, tmp); + float edgelength = vec3_length(tmp); + float curvature=0; + + // find the "sides" triangles that are on the edge uv + struct array sides; + array_init(&sides); + int i,j; + for (i = 0; iface.n; i++) { + if (HasVertex(Face(M, u, i), vid)) { + array_push(&sides, array_index(&u->face, i)); + } + } + // use the triangle facing most away from the sides + // to determine our curvature term + for (i = 0; iface.n; i++) { + float mincurv=1; // curve for face i and closer side to it + for (j = 0; jface, i))->normal, + Triangle(M, array_index(&sides,j))->normal); // use dot product of face normals. + float t = (1-dotprod)/2.0f; + if (t < mincurv) { + mincurv = t; + } + } + if (mincurv > curvature) + curvature = mincurv; + } + array_deinit(&sides); + // the more coplanar the lower the curvature term + return edgelength * curvature; +} + +static void +ComputeEdgeCostAtVertex(struct mesh *M, struct vertex *v) { + // compute the edge collapse cost for all edges that start + // from vertex v. Since we are only interested in reducing + // the object by selecting the min cost edge at each step, we + // only cache the cost of the least cost edge at this vertex + // (in member variable collapse) as well as the value of the + // cost (in member variable objdist). + if (v->neighbor.n == 0) { + // v doesn't have neighbors so it costs nothing to collapse + v->collapse=-1; + v->objdist=-0.01f; + return; + } + v->objdist = 1000000; + v->collapse=-1; + // search all neighboring edges for "least cost" edge + int i; + for (i = 0; ineighbor.n; i++) { + float dist; + dist = ComputeEdgeCollapseCost(M, v, array_index(&v->neighbor, i)); + if(distobjdist) { + v->collapse=array_index(&v->neighbor, i); // candidate for edge collapse + v->objdist=dist; // cost of the collapse + } + } +} + +static void +ComputeAllEdgeCollapseCosts(struct mesh *M) { + // For all the edges, compute the difference it would make + // to the model if it was collapsed. The least of these + // per vertex is cached in each vertex object. + int i; + for (i = 0; in_vertex; i++) { + ComputeEdgeCostAtVertex(M, Vertex(M, i)); + } +} + +static void +Collapse(struct mesh *M, int uid, int vid) { + // Collapse the edge uv by moving vertex u onto v + // Actually remove tris on uv, then update tris that + // have u to have v, and then remove u. + struct vertex *u = Vertex(M, uid); + if(vid < 0) { + // u is a vertex all by itself so just delete it + RemoveVertex(M, uid); + return; + } + + struct array tmp; + array_init(&tmp); + int i; + // make tmp a Array of all the neighbors of u + for (i = 0; ineighbor.n; i++) { + array_push(&tmp, array_index(&u->neighbor, i)); + } + + // delete triangles on edge uv: + { + i = u->face.n; + while (i--) { + if (HasVertex(Face(M, u, i), vid)) { + RemoveTriangle(M, array_index(&u->face, i)); + } + } + } + // update remaining triangles to have v instead of u + { + i = u->face.n; + while (i--) { + ReplaceVertex(M, array_index(&u->face, i), uid, vid); + } + } + RemoveVertex(M, uid); + // recompute the edge collapse costs for neighboring vertices + for (i = 0; in_vertex; i++) { + struct vertex *v = Vertex(M, i); + if (v->id >=0) { + if (mn == NULL || v->objdist < mn->objdist) { + mn = v; + } + } + } + return mn; +} + +void +ProgressiveMesh(int vert_n, int vert_stride, const float *v, int tri_n, const int *tri, int *map, int *permutation) { + struct mesh M; + mesh_init(&M, vert_n, tri_n); + + // put input data into our data structures M + int i; + const char * tmp = (const char *)v; + for (i=0;i=0; i--) { + // get the next vertex to collapse + struct vertex *mn = MinimumCostEdge(&M); + // keep track of this vertex, i.e. the collapse ordering + permutation[mn->id] = i; + // keep track of vertex to which we collapse to + map[i] = mn->collapse; + // Collapse this edge + Collapse(&M, mn->id, mn->collapse); + } + + // reorder the map Array based on the collapse ordering + for (i = 0; i