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

📄 mesh_chopper.cpp

📁 国外游戏开发者杂志2003年第七期配套代码
💻 CPP
📖 第 1 页 / 共 2 页
字号:
#include "framework.h"
#include "mesh_chopper.h"

#include "float.h"
#include "mesh.h"
#include "mesh_seam.h"
#include "mesh_builder.h"

#include <math.h>


int Vertex_Specifier::compare(Hashable *_other) {
    Vertex_Specifier *other = (Vertex_Specifier *)_other;

    if (other->input_n0 != input_n0) return -1;
    if (other->input_n1 != input_n1) return -1;

    // We intentionally don't compare 'output_index' since
    // it's an output parameter of the Hashable, not an input
    // parameter!  Well I guess that's sort of confusing, maybe
    // at some point I will try to make that clearer.
 

    return 0;
}
    
inline bool specs_match(Vertex_Specifier *spec_a, Vertex_Specifier *spec_b) {
    // @Speed: Could speed this up by checking the precomputed hash codes first

    int result = spec_a->compare(spec_b);
    if (result != 0) return false;
    return true;
}

Mesh_Chopper::Mesh_Chopper() {
    plane_epsilon = PLANE_EPSILON_DEFAULT;
}

Mesh_Chopper::~Mesh_Chopper() {
}

void Mesh_Chopper::set_plane_epsilon(float epsilon) {
    assert(epsilon >= 0);
    plane_epsilon = epsilon;
}

void Mesh_Chopper::do_an_edge_split(int n0, int n1, int c0, int c1,
                                    Vertex_Specifier *spec_return) {
    // If either end of the edge is on the plane,
    // we return that vertex instead of splitting the edge.
    // (This is an optimization, produces fewer triangles/vertices.
    // The optimization probably doesn't do much usually, but it
    // helps avoid pathological cases that would happen if you,
    // say, chop a regular grid by a plane that passes through
    // one of the grid lines.

    if (c0 == 0) {
        assert(c1 != 0);
        spec_return->input_n0 = n0;
        spec_return->input_n1 = n0;
        return;
    }

    if (c1 == 0) {
        spec_return->input_n0 = n1;
        spec_return->input_n1 = n1;
        return;
    }

    if (n0 < n1) {
        spec_return->input_n0 = n0;
        spec_return->input_n1 = n1;
    } else {
        spec_return->input_n0 = n1;
        spec_return->input_n1 = n0;
    }

    // This is a little slower than it needs to be
    // but hey, it's written for ease.

    Vector3 p0 = input_mesh->vertices[n0];
    Vector3 p1 = input_mesh->vertices[n1];

    Vector2 uv0 = input_mesh->uvs[n0];
    Vector2 uv1 = input_mesh->uvs[n1];

    Quaternion frame0 = input_mesh->tangent_frames[n0];
    Quaternion frame1 = input_mesh->tangent_frames[n1];

    
    float d0 = dot_product(plane_normal, p0) + plane_d;
    float d1 = dot_product(plane_normal, p1) + plane_d;
    
    float denom = d0 - d1;
    float t;
    if (denom) {
        t = d0 / denom;
    } else {
        t = 0;
    }

    assert(t >= 0);
    assert(t <= 1);

    Vector3 clip_point = p0 + (p1 - p0) * t;
    Vector2 clip_uv = uv0 + (uv1 - uv0) * t;

    float dot = dot_product(frame0, frame1);
    if (dot < 0) frame1 = frame1 * -1;
    Quaternion clip_frame = slerp(frame0, frame1, t);


    int index_a = intermediate_result_a.maybe_add_vertex(clip_point, clip_uv, clip_frame, spec_return);
    int index_b = intermediate_result_b.maybe_add_vertex(clip_point, clip_uv, clip_frame, spec_return);
}


int Mesh_Chopper::xref_vertex(Intermediate_Result *i_result,
                              Vertex_Specifier *spec) {
    int output_index;
    if (spec->input_n0 == spec->input_n1) {
        int index = spec->input_n0;
        output_index = i_result->maybe_add_vertex(input_mesh->vertices[index],
                                                  input_mesh->uvs[index],
                                                  input_mesh->tangent_frames[index],
                                                  spec);
    } else {
        Vertex_Specifier *found = (Vertex_Specifier *)i_result->vertex_hash_table->find(spec);
        assert(found);
        output_index = found->output_index;
    }

    return output_index;
}
/*
int Mesh_Chopper::xref_vertex(Intermediate_Result *i_result, int index) {
    
    if (index < 0) {
        return (-index) - 1;
    }

    Vertex_Specifier specifier(index, index);
    specifier.debug_condition_code = -42;
    int output_index = i_result->maybe_add_vertex(input_mesh->vertices[index],
                                                  input_mesh->uvs[index],
                                                  input_mesh->tangent_frames[index],
                                                  &specifier);
    return output_index;
}
*/
int Mesh_Chopper::xref_vertex_no_creation(Intermediate_Result *i_result, int index) {
    if (index < 0) {
        return (-index) - 1;
    }

    Vertex_Specifier specifier(index, index);
    int output_index = i_result->find_vertex(&specifier);
    return output_index;
}

void Mesh_Chopper::add_triangle(Vertex_Specifier *spec0,
                                Vertex_Specifier *spec1,
                                Vertex_Specifier *spec2,
                                Intermediate_Result *dest_result) {
    // Scan for degenerate triangles; refuse to add them...
    if (specs_match(spec0, spec1)) return;
    if (specs_match(spec1, spec2)) return;
    if (specs_match(spec2, spec0)) return;

    // Okay, add!

    int output_n0 = xref_vertex(dest_result, spec0);
    int output_n1 = xref_vertex(dest_result, spec1);
    int output_n2 = xref_vertex(dest_result, spec2);
    dest_result->mesh_builder->add_triangle(output_n0, output_n1, output_n2, current_material_index);
}

int Mesh_Chopper::classify_vertex(int index) {
    Vector3 position = input_mesh->vertices[index];
    float sum = dot_product(plane_normal, position) + plane_d;

    if (fabs(sum) <= plane_epsilon) return 0;
    if (sum < 0) return -1;
    return +1;
}

void Mesh_Chopper::rotate_indices(int *n0, int *n1, int *n2,
                                  int *c0, int *c1, int *c2) {
    int tmp;
    
    // This routine puts the indices into a sort of canonical
    // ordering based on their relationships to the plane.
    // This simplifies the number of cases we have to deal with
    // in the clipping code.

    while ((*c1 > *c0) || (*c2 > *c0)) {
        tmp = *n2;
        *n2 = *n1;
        *n1 = *n0;
        *n0 = tmp;

        tmp = *c2;
        *c2 = *c1;
        *c1 = *c0;
        *c0 = tmp;
    }

    while (*c2 >= 0) {
        tmp = *n0;
        *n0 = *n1;
        *n1 = *n2;
        *n2 = tmp;

        tmp = *c0;
        *c0 = *c1;
        *c1 = *c2;
        *c2 = tmp;
    }
}

void Mesh_Chopper::add_one_triangle(int n0, int n1, int n2) {
    int c0 = classify_vertex(n0);
    int c1 = classify_vertex(n1);
    int c2 = classify_vertex(n2);

    Intermediate_Result *dest_result = NULL;
    if ((c0 >= 0) && (c1 >= 0) && (c2 >= 0)) {
        dest_result = &intermediate_result_a;
    } else if ((c0 <= 0) && (c1 <= 0) && (c2 <= 0)) {
        dest_result = &intermediate_result_b;
    }

    // @Robustness:  Right now coplanar edges are not being
    // handled in this code... that should be fixed in the future 
    // please!

    if (dest_result) {
        Vertex_Specifier spec_n0(n0, n0);
        Vertex_Specifier spec_n1(n1, n1);
        Vertex_Specifier spec_n2(n2, n2);

        add_triangle(&spec_n0, &spec_n1, &spec_n2, dest_result);
        return;
    }


    rotate_indices(&n0, &n1, &n2, &c0, &c1, &c2);

    Vertex_Specifier spec_n0(n0, n0);
    Vertex_Specifier spec_n1(n1, n1);
    Vertex_Specifier spec_n2(n2, n2);

    spec_n0.debug_condition_code = c0;
    spec_n1.debug_condition_code = c1;
    spec_n2.debug_condition_code = c2;
    /*
      All the code below would probably get simpler if instead
      of using local indices n0, n1, n2, we just passed spec_0,
      spec_1, spec_2 to the various functions like add_triangle,
      and let those guys hash to look it up.  The downside is
      that would be slower.  Maybe we should make two fields
      on Vertex_Spec just to hold the output indices for
      result_a and result_b, we can pass the specs and not
      do a hash.  This would make the code more debuggable
      under maintenance!
    */

    // Register any coplanar edges.
    // This will do maybe not the right thing if c0 == c1 == c2 == 0...
    // need to think about what should really happen in that case.

    /* XXX Aren't all these double register_seam_edge calls redundant?
       Should just have one seam edge place centrally.  Too tired to
       investigate this right now though. 
    */
    if ((c0 == 0) && (c1 == 0)) {
        intermediate_result_a.register_seam_edge(&spec_n0, &spec_n1);
        intermediate_result_b.register_seam_edge(&spec_n0, &spec_n1);
        return;
    }

    if ((c1 == 0) && (c2 == 0)) {

⌨️ 快捷键说明

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