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

📄 mesh_reducer.cpp

📁 国外游戏开发者杂志2003年第七期配套代码
💻 CPP
📖 第 1 页 / 共 3 页
字号:
    } Endeach;
}


float Mesh_Reducer::find_hunt_radius_for_vertex(int vertex_index, Auto_Array <int> *targets) {
    float length_sum = 0;
    int length_count = 0;

    int other_index;
    Array_Foreach(targets, other_index) {
        length_count++;
        Vector3 delta1 = mesh->vertices[other_index] - mesh->vertices[vertex_index];
        length_sum += delta1.length();
    } Endeach;

    if (length_sum == 0) return 0; // @Think: something nonzero instead?

    float length_average = length_sum / (float)length_count;

    return length_average * hunt_radius_expansion_factor;
}


void Mesh_Reducer::update_best_candidates(int vertex_index,
                                          float *best_error_result,
                                          int *best_v1_result) {
    Vector3 *pos = &mesh->vertices[vertex_index];

    mark_vertex(vertex_index);

    Auto_Array <int> potential_targets;
    collect_and_mark_vertex_star(vertex_index, &potential_targets);

    float hunt_radius = find_hunt_radius_for_vertex(vertex_index, &potential_targets);

    int i0, i1, j0, j1, k0, k1;
    i0 = proximity_grid->get_index_x(pos->x - hunt_radius);
    i1 = proximity_grid->get_index_x(pos->x + hunt_radius);
    j0 = proximity_grid->get_index_y(pos->y - hunt_radius);
    j1 = proximity_grid->get_index_y(pos->y + hunt_radius);
    k0 = proximity_grid->get_index_z(pos->z - hunt_radius);
    k1 = proximity_grid->get_index_z(pos->z + hunt_radius);

    float r2 = hunt_radius * hunt_radius;

    int i, j, k;
    for (k = k0; k <= k1; k++) {
        for (j = j0; j <= j1; j++) {
            for (i = i0; i <= i1; i++) {
                int grid_index = proximity_grid->get_index(i, j, k);
                Lightweight_Proximity_Grid_Square *grid_square = &proximity_grid->grid_squares[grid_index];

                Auto_Array <Vector3 *> *vertices = &grid_square->vertices;

                int n;
                for (n = 0; n < vertices->live_items; n++) {
                    int other_index = get_vertex_index(mesh, vertices->data[n]);
                    if (!vertex_is_marked(other_index)) {
                        float d2 = distance_squared(*vertices->data[n], *pos);
                        if (d2 > r2) continue;

                        mark_vertex(other_index);
                        potential_targets.add(other_index);
                    }
                }
            }
        }
    }


    unmark_vertex(vertex_index);

    int other_index;
    Array_Foreach(&potential_targets, other_index) {
        unmark_vertex(other_index);
        update_best_candidates(vertex_index, other_index,
                               best_error_result, 
                               best_v1_result);
    } Endeach;

    if (*best_v1_result == -1) {
        int other_index;
        Array_Foreach(&potential_targets, other_index) {
            update_best_candidates(vertex_index, other_index,
                                   best_error_result, 
                                   best_v1_result);
        } Endeach;

    }

}

inline void Mesh_Reducer::update_single_vertex_without_queueing(int index,
                                                                float *error_result,
                                                                Reducer_Priority_Queue_Data *data_result) {
    data_result->vertex0 = index;
    data_result->vertex1 = -1;

    float best_error = FLT_MAX;
    update_best_candidates(index, &best_error, 
                           &data_result->vertex1);

    *error_result = best_error;
}

inline void Mesh_Reducer::queue_single_vertex(int index) {
    assert(mesh->canonical_vertex_map[index] == index);

    float best_error;
    Reducer_Priority_Queue_Data _data;
    update_single_vertex_without_queueing(index, &best_error, &_data);
    if ((_data.vertex1 != -1) && (best_error < FLT_MAX)) {
        Reducer_Priority_Queue_Data *data = new Reducer_Priority_Queue_Data;
        
        *data = _data;

        priority_queue->add(best_error, data);
    }
}


void Mesh_Reducer::init_priority_queue() {
    last_v0_examined = -1;
    last_v1_examined = -1;

    int i;
    for (i = 0; i < topology_handler->max_vertices; i++) {
        if (!(topology_handler->vertex_flags[i] & VERTEX_IS_LIVE)) continue;
        if (mesh->canonical_vertex_map[i] != i) continue;

        queue_single_vertex(i);
    }
}


int Mesh_Reducer::find_alias_to_map_to(int from_index, 
                                       int to_index) {
    int first_alias = to_index;
    int cursor = first_alias;

    int best_result = -1;
    float best_distance2 = FLT_MAX;

    float u0, v0;
    get_vertex_uv(mesh, from_index, &u0, &v0);
    
    // If we can find a vertex that represents uv coordinates within
    // the same material, map to that guy.
    while (1) {
        if (topology_handler->material_touched_by_vertex[from_index] ==
            topology_handler->material_touched_by_vertex[cursor]) {

            Vector3 tex0, tex1;
            get_vertex_uv(mesh, from_index, &tex0);
            get_vertex_uv(mesh, cursor, &tex1);

            float distance2 = (tex1 - tex0).length_squared();
            if (distance2 < best_distance2) {
                best_result = cursor;
                best_distance2 = distance2;
            }
        }

        // Go on to the next alias.

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

    if (best_result != -1) return best_result;

    // Give up, just give them an arbitrary one.  We could look into
    // creating a new vertex as a future extension, or moving this
    // one to the new position but... well... I just don't know.

    return to_index;
}


void Mesh_Reducer::perform_one_reduction() {
    int kill_v0 = -1;
    int kill_v1 = -1;

    float priority;
    Reducer_Priority_Queue_Data *data;
    while (1) {

        bool success = false;
        data = (Reducer_Priority_Queue_Data *)priority_queue->remove_head(&priority, &success);

        if (!success) {
            // @Improvement: Ugggh, this sucks, we need something better
            while (priority_queue->num_items == 0) {
                if (hunt_radius_expansion_factor > 10000) return;  // XXXX Give up!
                hunt_radius_expansion_factor *= 2;
                init_priority_queue();
            }

            continue;
        }

        assert(success);  // We must have one entry in queue per vertex
                          // on a face...

        int v0 = data->vertex0;
        int v1 = data->vertex1;

        if ((topology_handler->vertex_flags[v0] & VERTEX_IS_LIVE) && 
            (topology_handler->vertex_flags[v1] & VERTEX_IS_LIVE)) {
            // This is still potentially a valid collapse (both vertices still
            // exist) but we don't know whether other stuff has been collapsed
            // into V0, invalidating the priority computation that caused us
            // to just pull this guy off.  So.  We have this policy where we
            // always re-evaluate guys and throw them back on the queue.  If
            // we get the same guy twice in a row, we really want to process
            // him now, so away he goes.  Otherwise, he can wait.

            // Re-evaluate this guy and throw him back into the pond.

            float new_priority;
            update_single_vertex_without_queueing(v0, &new_priority, data);
            if (data->vertex1 == -1) {
                delete data;
                continue;
            }

            if (new_priority <= priority) { 
                kill_v0 = data->vertex0;
                kill_v1 = data->vertex1;

                delete data;
                break;
            } else {
                priority_queue->add(new_priority, data);
            }

            continue;
        }

        delete data;

        // Hmm, we nuked either the source or destination vertex since
        // that was completed...

        // If it was the source vertex, just continue.

        if (!(topology_handler->vertex_flags[v0] & VERTEX_IS_LIVE)) continue;

        // If on the other hand that guy is still alive, we need to
        // find a new place to tell him to go.  So find one, and
        // put it in the queue, and then keep on keepin' on.

        queue_single_vertex(v0);
    }

    perform_one_reduction(kill_v0, kill_v1);
}

void Mesh_Reducer::perform_one_reduction(int kill_v0, int kill_v1) {
    assert(kill_v1 >= 0);
    int first_alias = kill_v0;


    topology_handler->clear_faces_to_check();

    while (1) {
        int next = go_to_next_coincident_vertex(topology_handler, first_alias);
        assert(next != kill_v1);
        if (next == first_alias) break;

        int map_to = find_alias_to_map_to(next, kill_v1);

        // Accumulate quadrics.
        if (error_quadrics) {
            Error_Quadric *quadric0 = get_quadric(next);
            Error_Quadric *quadric1 = get_quadric(map_to);
            quadric1->accumulate_quadric(quadric0);
        }

        // Switch over the indices on all the faces.
        topology_handler->remap_vertex_face_indices(next, map_to);

        int next_next = go_to_next_coincident_vertex(topology_handler, next);
        topology_handler->vertex_coincidence_chain[first_alias] = next_next;
        topology_handler->vertex_coincidence_chain[next] = -1;

        topology_handler->eliminate_vertex_from_mesh(next);
    }

    topology_handler->vertex_coincidence_chain[first_alias] = -1;
    remove_vertex_from_grid(first_alias);
    int map_to = find_alias_to_map_to(first_alias, kill_v1);

    if (error_quadrics) { // If we are actually doing detail reduction...
        Error_Quadric *quadric0 = get_quadric(first_alias);
        Error_Quadric *quadric1 = get_quadric(map_to);

        quadric1->accumulate_quadric(quadric0);
    }

    topology_handler->remap_vertex_face_indices(first_alias, map_to);
    topology_handler->eliminate_vertex_from_mesh(first_alias);
    topology_handler->check_degenerate_faces();
}

void Mesh_Reducer::compensate_for_lonely_edge(Reducer_Face *face,
                                              int vertex_index0,
                                              int vertex_index1) {
    // Make some fake vertices that stick out from this face,
    // so's we like can pass them to the quadric thingy to
    // impose the constraint.

    // We start by getting the XYZ normal of the face.
    Vector3 position0;
    Vector3 position1;
    Vector3 position2;
    get_vertex_position(mesh, face->indices[0], &position0);
    get_vertex_position(mesh, face->indices[1], &position1);
    get_vertex_position(mesh, face->indices[2], &position2);

    Vector3 side1 = position1 - position0;
    Vector3 side2 = position2 - position0;
    Vector3 normal = cross_product(side1, side2);

    int can0 = mesh->canonical_vertex_map[vertex_index0];
    int can1 = mesh->canonical_vertex_map[vertex_index1];
    assert(topology_handler->vertex_flags[can0] & VERTEX_IS_ON_LONELY_EDGE);

⌨️ 快捷键说明

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