// File: tomfunkhouser[P]gaps/pkgs/R3Shapes/R3Mesh.cpp
// Function: gaps::R3Mesh::WritePlyStream(_IO_FILE*, int) const
int R3Mesh::
WritePlyStream(FILE *fp, RNBoolean binary)  const
{
  typedef struct PlyVertex {
    float x, y, z;
    float nx, ny, nz;
    float tx, ty;
    unsigned char red, green, blue;
  } PlyVertex;

  typedef struct PlyFace {
    unsigned char nverts;
    int *verts;
    int material;
    int segment;
    int category;
  } PlyFace;

  // Gather info about what vertex info to write
  RNBoolean has_vertex_normals = FALSE;
  RNBoolean has_vertex_texcoords = FALSE;
  RNBoolean has_vertex_colors = FALSE;
  for (int i = 0; i < NVertices(); i++) {
    R3MeshVertex *v = Vertex(i);
    if (v->flags[R3_MESH_VERTEX_NORMAL_UPTODATE]) has_vertex_normals = TRUE;
    if (!v->texcoords.IsZero()) has_vertex_texcoords = TRUE;
    if (!v->color.IsBlack()) has_vertex_colors = TRUE;
  }

  // Gather info about what face info to write
  RNBoolean has_face_materials = FALSE;
  RNBoolean has_face_segments = FALSE;
  RNBoolean has_face_categories = FALSE;
  for (int i = 0; i < NFaces(); i++) {
    R3MeshFace *f = Face(i);
    if (f->material != -1) has_face_materials = TRUE;
    if (f->segment != -1) has_face_segments = TRUE;
    if (f->category != -1) has_face_categories = TRUE;
  }
  
  // Element names
  char *elem_names[] = { (char *) "vertex", (char *) "face" };

  // List of property information for a vertex 
  static PlyProperty vert_props[] = { 
    {(char *) "x", PLY_FLOAT, PLY_FLOAT, offsetof(PlyVertex,x), 0, 0, 0, 0},
    {(char *) "y", PLY_FLOAT, PLY_FLOAT, offsetof(PlyVertex,y), 0, 0, 0, 0},
    {(char *) "z", PLY_FLOAT, PLY_FLOAT, offsetof(PlyVertex,z), 0, 0, 0, 0},
    {(char *) "nx", PLY_FLOAT, PLY_FLOAT, offsetof(PlyVertex,nx), 0, 0, 0, 0},
    {(char *) "ny", PLY_FLOAT, PLY_FLOAT, offsetof(PlyVertex,ny), 0, 0, 0, 0},
    {(char *) "nz", PLY_FLOAT, PLY_FLOAT, offsetof(PlyVertex,nz), 0, 0, 0, 0},
    {(char *) "tx", PLY_FLOAT, PLY_FLOAT, offsetof(PlyVertex,tx), 0, 0, 0, 0},
    {(char *) "ty", PLY_FLOAT, PLY_FLOAT, offsetof(PlyVertex,ty), 0, 0, 0, 0},
    {(char *) "red", PLY_UCHAR, PLY_UCHAR, offsetof(PlyVertex,red), 0, 0, 0, 0},
    {(char *) "green", PLY_UCHAR, PLY_UCHAR, offsetof(PlyVertex,green), 0, 0, 0, 0},
    {(char *) "blue", PLY_UCHAR, PLY_UCHAR, offsetof(PlyVertex,blue), 0, 0, 0, 0}
  };

  // List of property information for a vertex 
  static PlyProperty face_props[] = { 
    {(char *) "vertex_indices", PLY_INT, PLY_INT, offsetof(PlyFace,verts), 1, PLY_UCHAR, PLY_UCHAR, offsetof(PlyFace,nverts)},
    {(char *) "material_id", PLY_INT, PLY_INT, offsetof(PlyFace,material), 0, 0, 0, 0},
    {(char *) "segment_id", PLY_INT, PLY_INT, offsetof(PlyFace,segment), 0, 0, 0, 0},
    {(char *) "category_id", PLY_INT, PLY_INT, offsetof(PlyFace,category), 0, 0, 0, 0}
  };

  // Open ply file
  int file_type = (binary) ? PLY_BINARY_NATIVE : PLY_ASCII;
  PlyFile *ply = ply_write (fp, 2, elem_names, file_type);
  if (!ply) return 0;

  // Describe vertex properties
  ply_element_count(ply, (char *) "vertex", NVertices());
  ply_describe_property(ply, (char *) "vertex", &vert_props[0]);
  ply_describe_property(ply, (char *) "vertex", &vert_props[1]);
  ply_describe_property(ply, (char *) "vertex", &vert_props[2]);
  if (has_vertex_normals) {
    ply_describe_property(ply, (char *) "vertex", &vert_props[3]);
    ply_describe_property(ply, (char *) "vertex", &vert_props[4]);
    ply_describe_property(ply, (char *) "vertex", &vert_props[5]);
  }
  if (has_vertex_texcoords) {
    ply_describe_property(ply, (char *) "vertex", &vert_props[6]);
    ply_describe_property(ply, (char *) "vertex", &vert_props[7]);
  }
  if (has_vertex_colors) {
    ply_describe_property(ply, (char *) "vertex", &vert_props[8]);
    ply_describe_property(ply, (char *) "vertex", &vert_props[9]);
    ply_describe_property(ply, (char *) "vertex", &vert_props[10]);
  }

  // Describe face properties
  ply_element_count(ply, (char *) "face", NFaces());
  ply_describe_property(ply, (char *) "face", &face_props[0]);
  if (has_face_materials) {
    ply_describe_property(ply, (char *) "face", &face_props[1]);
  }
  if (has_face_segments) {
    ply_describe_property(ply, (char *) "face", &face_props[2]);
  }
  if (has_face_categories) {
    ply_describe_property(ply, (char *) "face", &face_props[3]);
  }
  
  // Complete header
  ply_header_complete(ply);

  // Write vertices
  ply_put_element_setup(ply, (char *) "vertex");
  for (int i = 0; i < NVertices(); i++) {
    R3MeshVertex *v = Vertex(i);
    const R3Point& p = VertexPosition(v);
    const R3Vector& n = VertexNormal(v);
    const R2Point& t = VertexTextureCoords(v);
    const RNRgb& c = VertexColor(v);
    PlyVertex ply_vertex;
    ply_vertex.x = p.X();
    ply_vertex.y = p.Y();
    ply_vertex.z = p.Z();
    ply_vertex.nx = n.X();
    ply_vertex.ny = n.Y();
    ply_vertex.nz = n.Z();
    ply_vertex.tx = t.X();
    ply_vertex.ty = t.Y();
    ply_vertex.red = (unsigned char) (255.0 * c.R());
    ply_vertex.green = (unsigned char) (255.0 * c.G());
    ply_vertex.blue = (unsigned char) (255.0 * c.B());
    ply_put_element(ply, (void *) &ply_vertex);
  }

  // Write faces
  ply_put_element_setup(ply, (char *) "face");
  for (int i = 0; i < NFaces(); i++) {
    R3MeshFace *f = Face(i);
    static int verts[3];
    static PlyFace ply_face = { 3, verts, 0 };
    ply_face.verts[0] = VertexID(VertexOnFace(f, 0));
    ply_face.verts[1] = VertexID(VertexOnFace(f, 1));
    ply_face.verts[2] = VertexID(VertexOnFace(f, 2));
    ply_face.material = FaceMaterial(f);
    ply_face.segment = FaceSegment(f);
    ply_face.category = FaceCategory(f);
    ply_put_element(ply, (void *) &ply_face);
  }

  // Free the memory
  free(ply);

  // Return success
  return 1;
}

// Assembly:
subq $0x128, %rsp            # imm = 0x128
movq %rdi, 0x118(%rsp)
movq %rsi, 0x110(%rsp)
movl %edx, 0x10c(%rsp)
movq 0x118(%rsp), %rax
movq %rax, 0x48(%rsp)
movl $0x0, 0x108(%rsp)
movl $0x0, 0x104(%rsp)
movl $0x0, 0x100(%rsp)
movl $0x0, 0xfc(%rsp)
movq 0x48(%rsp), %rdi
movl 0xfc(%rsp), %eax
movl %eax, 0x44(%rsp)
callq 0x135e0
movl %eax, %ecx
movl 0x44(%rsp), %eax
cmpl %ecx, %eax
jge 0x341c2
movq 0x48(%rsp), %rdi
movl 0xfc(%rsp), %esi
callq 0x13630
movq %rax, 0xf0(%rsp)
movq 0xf0(%rsp), %rax
addq $0x78, %rax
movq %rax, 0x38(%rsp)
leaq 0xe8(%rsp), %rdi
movl $0x2, %esi
callq 0x13d80
movq 0x38(%rsp), %rdi
movq 0xe8(%rsp), %rsi
callq 0x13d50
cmpl $0x0, %eax
je 0x34168
movl $0x1, 0x108(%rsp)
movq 0xf0(%rsp), %rdi
addq $0x48, %rdi
callq 0x3a3f0
cmpl $0x0, %eax
jne 0x34189
movl $0x1, 0x104(%rsp)
movq 0xf0(%rsp), %rdi
addq $0x58, %rdi
callq 0x3a440
cmpl $0x0, %eax
jne 0x341aa
movl $0x1, 0x100(%rsp)
jmp 0x341ac
movl 0xfc(%rsp), %eax
addl $0x1, %eax
movl %eax, 0xfc(%rsp)
jmp 0x340e7
movl $0x0, 0xe4(%rsp)
movl $0x0, 0xe0(%rsp)
movl $0x0, 0xdc(%rsp)
movl $0x0, 0xd8(%rsp)
movq 0x48(%rsp), %rdi
movl 0xd8(%rsp), %eax
movl %eax, 0x34(%rsp)
callq 0x135a0
movl %eax, %ecx
movl 0x34(%rsp), %eax
cmpl %ecx, %eax
jge 0x34296
movq 0x48(%rsp), %rdi
movl 0xd8(%rsp), %esi
callq 0x136d0
movq %rax, 0xd0(%rsp)
movq 0xd0(%rsp), %rax
cmpl $-0x1, 0x98(%rax)
je 0x34246
movl $0x1, 0xe4(%rsp)
movq 0xd0(%rsp), %rax
cmpl $-0x1, 0x9c(%rax)
je 0x34262
movl $0x1, 0xe0(%rsp)
movq 0xd0(%rsp), %rax
cmpl $-0x1, 0xa0(%rax)
je 0x3427e
movl $0x1, 0xdc(%rsp)
jmp 0x34280
movl 0xd8(%rsp), %eax
addl $0x1, %eax
movl %eax, 0xd8(%rsp)
jmp 0x341ee
movq 0x137453(%rip), %rax    # 0x16b6f0
movq %rax, 0xc0(%rsp)
movq 0x13744c(%rip), %rax    # 0x16b6f8
movq %rax, 0xc8(%rsp)
movl 0x10c(%rsp), %edx
movl $0x1, %eax
movl $0x4, %ecx
cmpl $0x0, %edx
cmovnel %ecx, %eax
movl %eax, 0xbc(%rsp)
movq 0x110(%rsp), %rdi
leaq 0xc0(%rsp), %rdx
movl 0xbc(%rsp), %ecx
movl $0x2, %esi
callq 0x71360
movq %rax, 0xb0(%rsp)
cmpq $0x0, 0xb0(%rsp)
jne 0x34316
movl $0x0, 0x124(%rsp)
jmp 0x348e6
movq 0x48(%rsp), %rdi
movq 0xb0(%rsp), %rax
movq %rax, 0x28(%rsp)
callq 0x135e0
movq 0x28(%rsp), %rdi
movl %eax, %edx
leaq 0xff227(%rip), %rsi     # 0x133562
callq 0x71e10
movq 0xb0(%rsp), %rdi
leaq 0xff213(%rip), %rsi     # 0x133562
leaq 0x13b49a(%rip), %rdx    # 0x16f7f0
callq 0x71aa0
movq 0xb0(%rsp), %rdi
leaq 0xff1f8(%rip), %rsi     # 0x133562
leaq 0x13b47f(%rip), %rdx    # 0x16f7f0
addq $0x28, %rdx
callq 0x71aa0
movq 0xb0(%rsp), %rdi
leaq 0xff1d9(%rip), %rsi     # 0x133562
leaq 0x13b460(%rip), %rdx    # 0x16f7f0
addq $0x50, %rdx
callq 0x71aa0
cmpl $0x0, 0x108(%rsp)
je 0x34406
movq 0xb0(%rsp), %rdi
leaq 0xff1b0(%rip), %rsi     # 0x133562
leaq 0x13b437(%rip), %rdx    # 0x16f7f0
addq $0x78, %rdx
callq 0x71aa0
movq 0xb0(%rsp), %rdi
leaq 0xff191(%rip), %rsi     # 0x133562
leaq 0x13b418(%rip), %rdx    # 0x16f7f0
addq $0xa0, %rdx
callq 0x71aa0
movq 0xb0(%rsp), %rdi
leaq 0xff16f(%rip), %rsi     # 0x133562
leaq 0x13b3f6(%rip), %rdx    # 0x16f7f0
addq $0xc8, %rdx
callq 0x71aa0
cmpl $0x0, 0x104(%rsp)
je 0x34454
movq 0xb0(%rsp), %rdi
leaq 0xff143(%rip), %rsi     # 0x133562
leaq 0x13b3ca(%rip), %rdx    # 0x16f7f0
addq $0xf0, %rdx
callq 0x71aa0
movq 0xb0(%rsp), %rdi
leaq 0xff121(%rip), %rsi     # 0x133562
leaq 0x13b3a8(%rip), %rdx    # 0x16f7f0
addq $0x118, %rdx            # imm = 0x118
callq 0x71aa0
cmpl $0x0, 0x100(%rsp)
je 0x344c4
movq 0xb0(%rsp), %rdi
leaq 0xff0f5(%rip), %rsi     # 0x133562
leaq 0x13b37c(%rip), %rdx    # 0x16f7f0
addq $0x140, %rdx            # imm = 0x140
callq 0x71aa0
movq 0xb0(%rsp), %rdi
leaq 0xff0d3(%rip), %rsi     # 0x133562
leaq 0x13b35a(%rip), %rdx    # 0x16f7f0
addq $0x168, %rdx            # imm = 0x168
callq 0x71aa0
movq 0xb0(%rsp), %rdi
leaq 0xff0b1(%rip), %rsi     # 0x133562
leaq 0x13b338(%rip), %rdx    # 0x16f7f0
addq $0x190, %rdx            # imm = 0x190
callq 0x71aa0
movq 0x48(%rsp), %rdi
movq 0xb0(%rsp), %rax
movq %rax, 0x20(%rsp)
callq 0x135a0
movq 0x20(%rsp), %rdi
movl %eax, %edx
leaq 0xff184(%rip), %rsi     # 0x13366d
callq 0x71e10
movq 0xb0(%rsp), %rdi
leaq 0xff170(%rip), %rsi     # 0x13366d
leaq 0x13b4ac(%rip), %rdx    # 0x16f9b0
callq 0x71aa0
cmpl $0x0, 0xe4(%rsp)
je 0x34532
movq 0xb0(%rsp), %rdi
leaq 0xff14b(%rip), %rsi     # 0x13366d
leaq 0x13b487(%rip), %rdx    # 0x16f9b0
addq $0x28, %rdx
callq 0x71aa0
cmpl $0x0, 0xe0(%rsp)
je 0x3455b
movq 0xb0(%rsp), %rdi
leaq 0xff122(%rip), %rsi     # 0x13366d
leaq 0x13b45e(%rip), %rdx    # 0x16f9b0
addq $0x50, %rdx
callq 0x71aa0
cmpl $0x0, 0xdc(%rsp)
je 0x34584
movq 0xb0(%rsp), %rdi
leaq 0xff0f9(%rip), %rsi     # 0x13366d
leaq 0x13b435(%rip), %rdx    # 0x16f9b0
addq $0x78, %rdx
callq 0x71aa0
movq 0xb0(%rsp), %rdi
callq 0x71e80
movq 0xb0(%rsp), %rdi
leaq 0xfefc2(%rip), %rsi     # 0x133562
callq 0x721b0
movl $0x0, 0xac(%rsp)
movq 0x48(%rsp), %rdi
movl 0xac(%rsp), %eax
movl %eax, 0x1c(%rsp)
callq 0x135e0
movl %eax, %ecx
movl 0x1c(%rsp), %eax
cmpl %ecx, %eax
jge 0x34799
movq 0x48(%rsp), %rdi
movl 0xac(%rsp), %esi
callq 0x13630
movq 0x48(%rsp), %rdi
movq %rax, 0xa0(%rsp)
movq 0xa0(%rsp), %rsi
callq 0x13660
movq 0x48(%rsp), %rdi
movq %rax, 0x98(%rsp)
movq 0xa0(%rsp), %rsi
callq 0x36860
movq 0x48(%rsp), %rdi
movq %rax, 0x90(%rsp)
movq 0xa0(%rsp), %rsi
callq 0x368f0
movq 0x48(%rsp), %rdi
movq %rax, 0x88(%rsp)
movq 0xa0(%rsp), %rsi
callq 0x368d0
movq %rax, 0x80(%rsp)
movq 0x98(%rsp), %rdi
callq 0x37820
cvtsd2ss %xmm0, %xmm0
movss %xmm0, 0x5c(%rsp)
movq 0x98(%rsp), %rdi
callq 0x37870
cvtsd2ss %xmm0, %xmm0
movss %xmm0, 0x60(%rsp)
movq 0x98(%rsp), %rdi
callq 0x378c0
cvtsd2ss %xmm0, %xmm0
movss %xmm0, 0x64(%rsp)
movq 0x90(%rsp), %rdi
callq 0x38c20
cvtsd2ss %xmm0, %xmm0
movss %xmm0, 0x68(%rsp)
movq 0x90(%rsp), %rdi
callq 0x38c30
cvtsd2ss %xmm0, %xmm0
movss %xmm0, 0x6c(%rsp)
movq 0x90(%rsp), %rdi
callq 0x38c40
cvtsd2ss %xmm0, %xmm0
movss %xmm0, 0x70(%rsp)
movq 0x88(%rsp), %rdi
callq 0x38c50
cvtsd2ss %xmm0, %xmm0
movss %xmm0, 0x74(%rsp)
movq 0x88(%rsp), %rdi
callq 0x38c60
cvtsd2ss %xmm0, %xmm0
movss %xmm0, 0x78(%rsp)
movq 0x80(%rsp), %rdi
callq 0x38cb0
movsd 0xfeaff(%rip), %xmm1    # 0x133220
movsd %xmm1, 0x10(%rsp)
mulsd %xmm1, %xmm0
cvttsd2si %xmm0, %eax
movb %al, 0x7c(%rsp)
movq 0x80(%rsp), %rdi
callq 0x38cc0
movsd 0x10(%rsp), %xmm1
mulsd %xmm1, %xmm0
cvttsd2si %xmm0, %eax
movb %al, 0x7d(%rsp)
movq 0x80(%rsp), %rdi
callq 0x38cd0
movsd 0x10(%rsp), %xmm1
mulsd %xmm1, %xmm0
cvttsd2si %xmm0, %eax
movb %al, 0x7e(%rsp)
movq 0xb0(%rsp), %rdi
leaq 0x5c(%rsp), %rsi
callq 0x72220
movl 0xac(%rsp), %eax
addl $0x1, %eax
movl %eax, 0xac(%rsp)
jmp 0x345b0
movq 0xb0(%rsp), %rdi
leaq 0xfeec5(%rip), %rsi     # 0x13366d
callq 0x721b0
movl $0x0, 0x58(%rsp)
movq 0x48(%rsp), %rdi
movl 0x58(%rsp), %eax
movl %eax, 0xc(%rsp)
callq 0x135a0
movl %eax, %ecx
movl 0xc(%rsp), %eax
cmpl %ecx, %eax
jge 0x348ce
movq 0x48(%rsp), %rdi
movl 0x58(%rsp), %esi
callq 0x136d0
movq 0x48(%rsp), %rdi
movq %rax, 0x50(%rsp)
movq 0x50(%rsp), %rsi
xorl %edx, %edx
callq 0x13700
movq 0x48(%rsp), %rdi
movq %rax, %rsi
callq 0x36910
movq 0x48(%rsp), %rdi
movl %eax, %ecx
movq 0x13b244(%rip), %rax    # 0x16fa58
movl %ecx, (%rax)
movq 0x50(%rsp), %rsi
movl $0x1, %edx
callq 0x13700
movq 0x48(%rsp), %rdi
movq %rax, %rsi
callq 0x36910
movq 0x48(%rsp), %rdi
movl %eax, %ecx
movq 0x13b218(%rip), %rax    # 0x16fa58
movl %ecx, 0x4(%rax)
movq 0x50(%rsp), %rsi
movl $0x2, %edx
callq 0x13700
movq 0x48(%rsp), %rdi
movq %rax, %rsi
callq 0x36910
movq 0x48(%rsp), %rdi
movl %eax, %ecx
movq 0x13b1eb(%rip), %rax    # 0x16fa58
movl %ecx, 0x8(%rax)
movq 0x50(%rsp), %rsi
callq 0x369e0
movq 0x48(%rsp), %rdi
movl %eax, 0x13b1db(%rip)    # 0x16fa60
movq 0x50(%rsp), %rsi
callq 0x36a20
movq 0x48(%rsp), %rdi
movl %eax, 0x13b1ca(%rip)    # 0x16fa64
movq 0x50(%rsp), %rsi
callq 0x36a60
movl %eax, 0x13b1be(%rip)    # 0x16fa68
movq 0xb0(%rsp), %rdi
leaq 0x13b197(%rip), %rsi    # 0x16fa50
callq 0x72220
movl 0x58(%rsp), %eax
addl $0x1, %eax
movl %eax, 0x58(%rsp)
jmp 0x347b5
movq 0xb0(%rsp), %rdi
callq 0xf5f0
movl $0x1, 0x124(%rsp)
movl 0x124(%rsp), %eax
addq $0x128, %rsp            # imm = 0x128
retq
nopw %cs:(%rax,%rax)