⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 mesh_reducer.cpp

📁 国外游戏开发者杂志2003年第七期配套代码
💻 CPP
📖 第 1 页 / 共 3 页
字号:
// @Cleanliness: Lightweight_Proximity_Grid should have an
// abstracted find() function.  That would be nice.

#include "../framework.h"
#include "mesh_reducer.h"
#include "lightweight_proximity_grid.h"
#include "priority_queue.h"
#include "../mesh.h"
#include "error_quadric.h"
#include "mesh_topology_handler.h"
#include "mesh_builder.h"

#include <float.h>
#include <math.h>

extern float GetArea(Vector3 v0, Vector3 v1, Vector3 v2);

struct Reducer_Priority_Queue_Data {
    int vertex0;
    int vertex1;
};

inline int go_to_next_coincident_vertex(Mesh_Topology_Handler *handler, int cursor) {
    assert(cursor < handler->mesh->num_vertices);
    int next = handler->vertex_coincidence_chain[cursor];
    return next;
}

void get_vertex_uv(Triangle_List_Mesh *mesh, int index, Vector3 *result) {
    result->x = mesh->uvs[index].x;
    result->y = mesh->uvs[index].y;
    result->z = 0;
}

void get_vertex_uv(Triangle_List_Mesh *mesh, int index, float *u_ret, float *v_ret) {
    *u_ret = mesh->uvs[index].x;
    *v_ret = mesh->uvs[index].y;
}

void get_vertex_position(Triangle_List_Mesh *mesh, int index, Vector3 *result) {
    *result = mesh->vertices[index];
}

void Mesh_Reducer::fill_point(float *point, int vertex_index, int material_index) {
    float uv_weight = tuning.texture_space_importance_factor;

    if (material_index != -1) {
        float specific_material_factor = material_factor_uv_over_xyz[material_index];
        uv_weight *= specific_material_factor;
    }

    point[0] = mesh->vertices[vertex_index].x;
    point[1] = mesh->vertices[vertex_index].y;
    point[2] = mesh->vertices[vertex_index].z;
    point[3] = mesh->uvs[vertex_index].x * uv_weight;
    point[4] = mesh->uvs[vertex_index].y * uv_weight;
}



void Mesh_Reducer::count_material_areas() {
    // Initialize the ratio of UV space to XYZ space for
    // each material.

    // First build the material arrays and initialize them to 0.

    material_area_uv = new float[mesh->num_materials];
    material_area_xyz = new float[mesh->num_materials];
    material_factor_uv_over_xyz = new float[mesh->num_materials];

    int i;
    for (i = 0; i < mesh->num_materials; i++) {
        material_area_uv[i] = 0;
        material_area_xyz[i] = 0;
        material_factor_uv_over_xyz[i] = 0;
    }

    // Now we iterate over all the faces and add up corresponding
    // areas.  Etc.

    for (i = 0; i < mesh->num_faces; i++) {
        Reducer_Face *face = &topology_handler->faces[i];
        int material_index = face->material;

        int N0 = face->indices[0];
        int N1 = face->indices[1];
        int N2 = face->indices[2];

        Vector3 pos0, pos1, pos2;
        Vector3 tex0, tex1, tex2;

        get_vertex_position(mesh, N0, &pos0);
        get_vertex_position(mesh, N1, &pos1);
        get_vertex_position(mesh, N2, &pos2);

        get_vertex_uv(mesh, N0, &tex0);
        get_vertex_uv(mesh, N1, &tex1);
        get_vertex_uv(mesh, N2, &tex2);

        float XYZArea = GetArea(pos0, pos1, pos2);
        float UVArea = GetArea(tex0, tex1, tex2);

        material_area_xyz[material_index] += XYZArea;
        material_area_uv[material_index] += UVArea;
    }

    for (i = 0; i < mesh->num_materials; i++) {
        if (material_area_uv[i]) {
            material_factor_uv_over_xyz[i] = sqrt(material_area_xyz[i] / material_area_uv[i]);
        } else {
            material_factor_uv_over_xyz[i] = 0;
        }
    }
}


void Mesh_Reducer::add_face_constraint_to_quadrics(Reducer_Face *face) {
    float point0[ERROR_QUADRIC_DIMENSIONS];
    float point1[ERROR_QUADRIC_DIMENSIONS];
    float point2[ERROR_QUADRIC_DIMENSIONS];

    fill_point(point0, face->indices[0], face->material);
    fill_point(point1, face->indices[1], face->material);
    fill_point(point2, face->indices[2], face->material);

    Error_Quadric *quadric0 = get_quadric(face->indices[0]);
    Error_Quadric *quadric1 = get_quadric(face->indices[1]);
    Error_Quadric *quadric2 = get_quadric(face->indices[2]);

    quadric0->accumulate_plane(point0, point1, point2);
    quadric1->accumulate_plane(point0, point1, point2);
    quadric2->accumulate_plane(point0, point1, point2);
}

void Mesh_Reducer::init_quadrics() {
    count_material_areas();
    error_quadrics = new Error_Quadric[mesh->num_vertices];

    int i;
    for (i = 0; i < mesh->num_vertices; i++) {
        error_quadrics[i].clear();
    }

    error_quadrics[0].init_index_table();  // Set up the static data.

    for (i = 0; i < topology_handler->num_faces_remaining; i++) {
        Reducer_Face *face = &topology_handler->faces[i];

        if (!(face->flags & FACE_IS_A_SEAM_FILL)) {
            add_face_constraint_to_quadrics(&topology_handler->faces[i]);
        }
    }
}

Error_Quadric *Mesh_Reducer::get_quadric(int index) {
    return &error_quadrics[index];
}

Mesh_Reducer::Mesh_Reducer() {
    tuning.material_boundary_penalty = 1000.0f;
    tuning.topology_change_penalty = 2.0f;
    tuning.icky_face_penalty = 1000.0f;
    tuning.texture_space_importance_factor = 2.0f;
    tuning.lonely_edge_constraint_factor = 3.0f;

    mesh = NULL;
    priority_queue = NULL;
    proximity_grid = NULL;
    error_quadrics = NULL;

    num_target_faces = 1000;
    num_lonely_edges_detected = 0;
    hunt_radius_expansion_factor = 1;

    topology_handler = NULL;
}

Mesh_Reducer::~Mesh_Reducer() {
    delete proximity_grid;
    delete priority_queue;
    delete topology_handler;
    delete [] error_quadrics;
    delete [] material_area_uv;
    delete [] material_area_xyz;
    delete [] material_factor_uv_over_xyz;
}

float Mesh_Reducer::compute_collapse_error(int index0, int index1) {
    // Step through every alias of index0.  Find best match
    // among aliases of index1.  Compute error due to the move,
    // accumulate to sum.

    // When done, divide sum by number of index0 aliases to
    // compute an average.

    float error_sum = 0;
    float penalty = 1;

    int first_alias = index0;
    int cursor = index0;
    int vertices_considered = 0;

    while (1) {
        int dest = topology_handler->simple_find_alias_to_map_to(cursor, index1);
        if (dest == -1) {
            // Material boundary detected.  There's no right answer
            // here, so let's just collapse to the canonical vertex,
            // and jack this reduction by a big penalty.  
            // (@Improvement: We might
            // in future want to try to find the vertex with the 
            // least amount of error, or something.)
            
            dest = index1;
            penalty = tuning.material_boundary_penalty;
        }

        Error_Quadric *quadric0 = get_quadric(cursor);
        Error_Quadric *quadric1 = get_quadric(dest);
        assert(quadric0 != quadric1);

        float pos_1_array[ERROR_QUADRIC_DIMENSIONS];
        fill_point(pos_1_array, dest, topology_handler->material_touched_by_vertex[dest]);

        float error = quadric0->evaluate_error(pos_1_array);
        error_sum += error;

        vertices_considered++;

        cursor = topology_handler->vertex_coincidence_chain[cursor];
        if (cursor == first_alias) break;
    }

    if (vertices_considered == 0) return 0;

    if (tuning.topology_change_penalty) {
        const float TOPOLOGY_CHANGE_EPSILON = 0.5f; // Arbitrary
        if (topology_handler->collapse_changes_topology(index0, index1)) {
            error_sum += TOPOLOGY_CHANGE_EPSILON;
            error_sum *= tuning.topology_change_penalty;
        }
    }

    if (topology_handler->collapse_creates_icky_face(index0, index1)) {
        return FLT_MAX;
    }

    return (error_sum * penalty) / (float)vertices_considered;
//    return (error_sum * penalty);
}

void Mesh_Reducer::update_best_candidates(int index0,
                                          int index1,
                                          float *best_error_result,
                                          int *best_v1_result) {
    if (index0 == index1) return;

    assert(topology_handler->vertex_flags[index0] & VERTEX_IS_LIVE);
    assert(topology_handler->vertex_flags[index1] & VERTEX_IS_LIVE);

    float Error = compute_collapse_error(index0, index1);

    if (Error < *best_error_result) {
        *best_error_result = Error;
        *best_v1_result = index1;
    }
}

int get_vertex_index(Triangle_List_Mesh *mesh, Vector3 *pos) {
    int index = pos - mesh->vertices;
    assert(index >= 0);
    assert(index < mesh->num_vertices);

    return index;
}

inline int get_index_within_face(Triangle_List_Mesh *mesh, Reducer_Face *face, int vertex_index) {
    int *canonical = mesh->canonical_vertex_map;
    assert(canonical[vertex_index] == vertex_index);

    int where_within_face;

    if (canonical[face->indices[0]] == vertex_index) where_within_face = 0;
    else if (canonical[face->indices[1]] == vertex_index) where_within_face = 1;
    else if (canonical[face->indices[2]] == vertex_index) where_within_face = 2;
    else where_within_face = -1;

    return where_within_face;
}

inline bool Mesh_Reducer::vertex_is_marked(int index) {
    return topology_handler->vertex_flags[index] & VERTEX_IS_MARKED;
}

inline void Mesh_Reducer::mark_vertex(int index) {
    topology_handler->vertex_flags[index] |= VERTEX_IS_MARKED;
}

inline void Mesh_Reducer::unmark_vertex(int index) {
    topology_handler->vertex_flags[index] &= ~VERTEX_IS_MARKED;
}


void Mesh_Reducer::collect_and_mark_vertex_star(int vertex_index, Auto_Array <int> *targets) {
    Reducer_Face_Membership *membership = &topology_handler->face_membership[vertex_index];

    int face_index;
    Array_Foreach(&membership->faces, face_index) {
        Reducer_Face *face = &topology_handler->faces[face_index];
        int within = get_index_within_face(mesh, face, vertex_index);
        assert(within != -1);

        int n1 = face->indices[(within + 1) % 3];
        int n2 = face->indices[(within + 2) % 3];

        n1 = mesh->canonical_vertex_map[n1];
        n2 = mesh->canonical_vertex_map[n2];

        if (!vertex_is_marked(n1)) {
            targets->add(n1);
            mark_vertex(n1);
        }


        if (!vertex_is_marked(n2)) {
            targets->add(n2);
            mark_vertex(n2);
        }

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -