// File: tomfunkhouser[P]gaps/pkgs/R3Surfels/R3SurfelBlock.cpp
// Function: gaps::R3SurfelBlock::Draw(gaps::RNFlags, int) const
void R3SurfelBlock::
Draw(RNFlags flags, int subsampling_factor) const
{
  // Just checking
  if (nsurfels == 0) return;
  if (subsampling_factor < 1) subsampling_factor = 1;
  int c = flags[R3_SURFEL_COLOR_DRAW_FLAG];
  
  // Push translation to position_origin
  glPushMatrix();
  glTranslated(position_origin[0], position_origin[1], position_origin[2]);

#if (R3_SURFEL_BLOCK_DRAW_METHOD == R3_SURFEL_BLOCK_DRAW_WITH_DISPLAY_LIST)
  // Create a display list id to use to detect errors
  static GLuint error_id = 0;
  if (error_id == 0) error_id = glGenLists(1);

  // Create display list for block
  if (opengl_id == 0) {
    R3SurfelBlock *block = (R3SurfelBlock *) this;
    block->opengl_id = error_id;
    glGetError();
    GLuint id = glGenLists(2);
    if ((id > 0) && (glGetError() == GL_NO_ERROR)) {
      glNewList(id, GL_COMPILE);
#     if 1
        // Draw without color using arrays
        glEnableClientState(GL_VERTEX_ARRAY);
        glVertexPointer(3, GL_FLOAT, sizeof(R3Surfel), surfels);
        glDrawArrays(GL_POINTS, 0, NSurfels());
        glDisableClientState(GL_VERTEX_ARRAY);
#     else
        // Draw without color using RNGrfxBegin and RNGrfxEnd
        RNGrfxBegin(RN_GRFX_POINTS);
        for (int i = 0; i < NSurfels(); i++) {
          const R3Surfel& surfel = surfels[i];
          R3LoadPoint(surfel.PositionPtr());
        }
        RNGrfxEnd();
#     endif
      RNGrfxEndList();
      if (glGetError() == GL_NO_ERROR) {
        glNewList(id+1, GL_COMPILE);
#       if 0
          // Draw with color using arrays
          glEnableClientState(GL_VERTEX_ARRAY);
          glEnableClientState(GL_COLOR_ARRAY);
          glVertexPointer(3, GL_FLOAT, sizeof(R3Surfel), surfels);
          glColorPointer(3, GL_UNSIGNED_BYTE, sizeof(R3Surfel), surfels[0].ColorPtr());
          glDrawArrays(GL_POINTS, 0, NSurfels());
          glDisableClientState(GL_VERTEX_ARRAY);
          glDisableClientState(GL_COLOR_ARRAY);
#       else
          // Draw with color using RNGrfxBegin and RNGrfxEnd
          RNGrfxBegin(RN_GRFX_POINTS);
          for (int i = 0; i < NSurfels(); i++) {
            const R3Surfel& surfel = surfels[i];
            RNLoadRgb(surfel.ColorPtr());
            R3LoadPoint(surfel.PositionPtr());
          }
          RNGrfxEnd();
#       endif
        RNGrfxEndList();
        if (glGetError() == GL_NO_ERROR) {
          block->opengl_id = id;
        }
      }
      if (opengl_id == error_id) {
        glDeleteLists(id, 2);
      }
    }
  }

  // Draw surfels
  if (opengl_id != error_id) {
    // Use display list if available
    glCallList((c) ? opengl_id + 1 : opengl_id);
  }
  else {
    // Draw surfels the slow way
    if (c) {
      // Draw surfels (with color)
      RNGrfxBegin(RN_GRFX_POINTS);
      for (int i = 0; i < NSurfels(); i++) {
        const R3Surfel& surfel = surfels[i];
        RNLoadRgb(surfel.ColorPtr());
        R3LoadPoint(surfel.PositionPtr());
      }
      RNGrfxEnd();
    }
    else {
      // Draw surfels without color
      RNGrfxBegin(RN_GRFX_POINTS);
      for (int i = 0; i < NSurfels(); i++) {
        const R3Surfel& surfel = surfels[i];
        R3LoadPoint(surfel.PositionPtr());
      }
      RNGrfxEnd();
    }
  }
#elif (R3_SURFEL_BLOCK_DRAW_METHOD == R3_SURFEL_BLOCK_DRAW_WITH_VBO)
  // Create a VBO id to use to detect errors
  static GLuint error_buffer_id = 0;
  if (error_buffer_id == 0) {
    glGenBuffers(1, &error_buffer_id);
    if (glGetError() != GL_NO_ERROR) {
      printf("Error in glGenBuffers\n");
    }
  }
  
  // Create a VBO for block
  if (opengl_id == 0) {
    GLuint buffer_id;
    ((R3SurfelBlock *) this)->opengl_id = error_buffer_id;
    glGenBuffers(1, &buffer_id);
    if (buffer_id > 0) {
      glBindBuffer(GL_ARRAY_BUFFER, buffer_id);
      glBufferData(GL_ARRAY_BUFFER, NSurfels() * sizeof(R3Surfel), surfels, GL_STATIC_DRAW);
      if (glGetError() == GL_NO_ERROR) {
        ((R3SurfelBlock *) this)->opengl_id = buffer_id;
      }
    }
  }

  // Draw surfels
  glEnableClientState(GL_VERTEX_ARRAY);
  if (c) glEnableClientState(GL_COLOR_ARRAY);
  else glDisableClientState(GL_COLOR_ARRAY);
  if (opengl_id != error_buffer_id) {
    // Draw surfels using VBO arrays
    static R3Surfel tmp;
    static unsigned long int position_offset = (unsigned char *) tmp.PositionPtr() - (unsigned char *) &tmp;
    static unsigned long int color_offset = (unsigned char *) tmp.ColorPtr() - (unsigned char *) &tmp;
    glBindBuffer(GL_ARRAY_BUFFER, opengl_id);
    glVertexPointer(3, GL_FLOAT, sizeof(R3Surfel), (const void *) position_offset);
    if (c) glColorPointer(3, GL_UNSIGNED_BYTE, sizeof(R3Surfel), (const void *)color_offset);
  }
  else {
    // Draw surfels using client-side arrays
    glVertexPointer(3, GL_FLOAT, sizeof(R3Surfel), surfels[0].PositionPtr());
    if (c) glColorPointer(3, GL_UNSIGNED_BYTE, sizeof(R3Surfel), surfels[0].ColorPtr());
  }
  glDrawArrays(GL_POINTS, 0, NSurfels());
#elif (R3_SURFEL_BLOCK_DRAW_METHOD == R3_SURFEL_BLOCK_DRAW_WITH_ARRAYS)
  // Draw surfels using arrays
  glEnableClientState(GL_VERTEX_ARRAY);
  if (c) glEnableClientState(GL_COLOR_ARRAY);
  else glDisableClientState(GL_COLOR_ARRAY);
  glVertexPointer(3, GL_FLOAT, sizeof(R3Surfel), surfels[0].PositionPtr());
  if (c) glColorPointer(3, GL_UNSIGNED_BYTE, sizeof(R3Surfel), surfels[0].ColorPtr());
  glDrawArrays(GL_POINTS, 0, NSurfels());
#else
  // Get convenient variables
  int n = flags[R3_SURFEL_NORMAL_DRAW_FLAG];
  int id = flags[R3_SURFEL_IDENTIFIER_DRAW_FLAG];

  // Draw surfels using RNGrfxBegin ... RNGrfxEnd
  if (flags[R3_SURFEL_DISC_DRAW_FLAG] && HasNormals() && HasTangents()) {
    // Draw discs
    const int nsides = 6;
    RNGrfxBegin(RN_GRFX_TRIANGLES);
    for (int i = 0; i < NSurfels(); i += subsampling_factor) {
      const R3Surfel& surfel = surfels[i];
      R3Vector normal(surfel.NX(), surfel.NY(), surfel.NZ());
      R3Vector tangent1(surfel.TX(), surfel.TY(), surfel.TZ());
      R3Vector tangent2 = normal % tangent1;
      double r1 = surfel.Radius(0);
      double r2 = surfel.Radius(1);
      if (r1 <= 0) r1 = 0.1;
      if (r2 <= 0) r2 = r1;
      if (c) RNLoadRgb(surfel.ColorPtr());
      if (id) RNLoadRgba(surfel.Identifier());
      else if (n) R3LoadNormal(normal);
      R3Point p[nsides];
      for (int j = 0; j < nsides; j++) {
        double angle = RN_TWO_PI*j/nsides;
        p[j].Reset(surfel.PX(), surfel.PY(), surfel.PZ());
        p[j] += cos(angle) * r1 * tangent1;
        p[j] += sin(angle) * r2 * tangent2;
      }
      for (int j = 0; j < nsides; j++) {
        R3LoadPoint(surfel.PositionPtr());
        R3LoadPoint(p[j]);
        R3LoadPoint(p[(j+1)%nsides]);
      }
    }
    RNGrfxEnd();        
  }
  else {
    // Draw points
    RNGrfxBegin(RN_GRFX_POINTS);
    for (int i = 0; i < NSurfels(); i += subsampling_factor) {
      const R3Surfel& surfel = surfels[i];
      if (c) RNLoadRgb(surfel.ColorPtr());
      if (id) RNLoadRgba(surfel.Identifier());
      else if (n) R3LoadNormal(surfel.NX(), surfel.NY(), surfel.NZ());
      R3LoadPoint(surfel.PositionPtr());
    }
    RNGrfxEnd();
  }
#endif
  
  // Pop translation to position_origin
  glPopMatrix();
}

// Assembly:
subq $0x218, %rsp            # imm = 0x218
movq %rsi, 0x210(%rsp)
movq %rdi, 0x208(%rsp)
movl %edx, 0x204(%rsp)
movq 0x208(%rsp), %rax
movq %rax, 0x78(%rsp)
cmpl $0x0, 0x8(%rax)
jne 0x4c596
jmp 0x4cc67
cmpl $0x1, 0x204(%rsp)
jge 0x4c5ab
movl $0x1, 0x204(%rsp)
leaq 0x1f8(%rsp), %rdi
movl $0x1, %esi
callq 0x1b970
movq 0x1f8(%rsp), %rsi
leaq 0x210(%rsp), %rdi
callq 0x31a20
movl %eax, 0x200(%rsp)
callq 0x138d0
movq 0x78(%rsp), %rdi
addq $0x10, %rdi
xorl %esi, %esi
callq 0x1bba0
movq 0x78(%rsp), %rdi
movsd %xmm0, 0x70(%rsp)
addq $0x10, %rdi
movl $0x1, %esi
callq 0x1bba0
movq 0x78(%rsp), %rdi
movsd %xmm0, 0x68(%rsp)
addq $0x10, %rdi
movl $0x2, %esi
callq 0x1bba0
movsd 0x68(%rsp), %xmm1
movaps %xmm0, %xmm2
movsd 0x70(%rsp), %xmm0
callq 0x135e0
leaq 0x1e8(%rsp), %rdi
movl $0x2, %esi
callq 0x1b970
movq 0x1e8(%rsp), %rsi
leaq 0x210(%rsp), %rdi
callq 0x31a20
movl %eax, 0x1f4(%rsp)
leaq 0x1d8(%rsp), %rdi
movl $0x4, %esi
callq 0x1b970
movq 0x1d8(%rsp), %rsi
leaq 0x210(%rsp), %rdi
callq 0x31a20
movl %eax, 0x1e4(%rsp)
leaq 0x1d0(%rsp), %rdi
movl $0x10, %esi
callq 0x1b970
movq 0x1d0(%rsp), %rsi
leaq 0x210(%rsp), %rdi
callq 0x31a20
cmpl $0x0, %eax
je 0x4cb48
movq 0x78(%rsp), %rdi
callq 0x4afc0
cmpl $0x0, %eax
je 0x4cb48
movq 0x78(%rsp), %rdi
callq 0x4b050
cmpl $0x0, %eax
je 0x4cb48
movl $0x6, 0x1cc(%rsp)
movl $0x4, %edi
callq 0x4ff50
movl $0x0, 0x1c8(%rsp)
movq 0x78(%rsp), %rdi
movl 0x1c8(%rsp), %eax
movl %eax, 0x64(%rsp)
callq 0x1c0e0
movl %eax, %ecx
movl 0x64(%rsp), %eax
cmpl %ecx, %eax
jge 0x4cb3e
movq 0x78(%rsp), %rax
movq (%rax), %rax
movslq 0x1c8(%rsp), %rcx
imulq $0x30, %rcx, %rcx
addq %rcx, %rax
movq %rax, 0x1c0(%rsp)
movq 0x1c0(%rsp), %rdi
callq 0x31fb0
cvtss2sd %xmm0, %xmm0
movsd %xmm0, 0x48(%rsp)
movq 0x1c0(%rsp), %rdi
callq 0x31fd0
cvtss2sd %xmm0, %xmm0
movsd %xmm0, 0x40(%rsp)
movq 0x1c0(%rsp), %rdi
callq 0x31ff0
movsd 0x40(%rsp), %xmm1
movaps %xmm0, %xmm2
movsd 0x48(%rsp), %xmm0
cvtss2sd %xmm2, %xmm2
leaq 0x1a8(%rsp), %rdi
callq 0xf94b0
movq 0x1c0(%rsp), %rdi
callq 0x32010
cvtss2sd %xmm0, %xmm0
movsd %xmm0, 0x58(%rsp)
movq 0x1c0(%rsp), %rdi
callq 0x32030
cvtss2sd %xmm0, %xmm0
movsd %xmm0, 0x50(%rsp)
movq 0x1c0(%rsp), %rdi
callq 0x32050
movsd 0x50(%rsp), %xmm1
movaps %xmm0, %xmm2
movsd 0x58(%rsp), %xmm0
cvtss2sd %xmm2, %xmm2
leaq 0x190(%rsp), %rdi
callq 0xf94b0
leaq 0x178(%rsp), %rdi
leaq 0x1a8(%rsp), %rsi
leaq 0x190(%rsp), %rdx
callq 0xfa2f0
movq 0x1c0(%rsp), %rdi
xorl %esi, %esi
callq 0x32070
cvtss2sd %xmm0, %xmm0
movsd %xmm0, 0x170(%rsp)
movq 0x1c0(%rsp), %rdi
movl $0x1, %esi
callq 0x32070
cvtss2sd %xmm0, %xmm0
movsd %xmm0, 0x168(%rsp)
xorps %xmm0, %xmm0
ucomisd 0x170(%rsp), %xmm0
jb 0x4c875
movsd 0x157b2c(%rip), %xmm0   # 0x1a4398
movsd %xmm0, 0x170(%rsp)
xorps %xmm0, %xmm0
ucomisd 0x168(%rsp), %xmm0
jb 0x4c895
movsd 0x170(%rsp), %xmm0
movsd %xmm0, 0x168(%rsp)
cmpl $0x0, 0x200(%rsp)
je 0x4c8b4
movq 0x1c0(%rsp), %rdi
callq 0x4ff80
movq %rax, %rdi
callq 0x4ff70
cmpl $0x0, 0x1e4(%rsp)
je 0x4c8d4
movq 0x1c0(%rsp), %rdi
callq 0x31f20
movl %eax, %edi
callq 0x4ff90
jmp 0x4c8ed
cmpl $0x0, 0x1f4(%rsp)
je 0x4c8eb
leaq 0x1a8(%rsp), %rdi
callq 0x4ffe0
jmp 0x4c8ed
leaq 0xd0(%rsp), %rax
movq %rax, %rcx
addq $0x90, %rcx
movq %rcx, 0x30(%rsp)
movq %rax, 0x38(%rsp)
movq 0x38(%rsp), %rdi
movq %rdi, 0x28(%rsp)
callq 0xf8340
movq 0x28(%rsp), %rax
movq 0x30(%rsp), %rcx
addq $0x18, %rax
cmpq %rcx, %rax
movq %rax, 0x38(%rsp)
jne 0x4c909
movl $0x0, 0xcc(%rsp)
cmpl $0x6, 0xcc(%rsp)
jge 0x4caa0
leaq 0x15c9d0(%rip), %rax    # 0x1a9320
movsd (%rax), %xmm0
cvtsi2sdl 0xcc(%rsp), %xmm1
mulsd %xmm1, %xmm0
movsd 0x157a37(%rip), %xmm1   # 0x1a43a0
divsd %xmm1, %xmm0
movsd %xmm0, 0xc0(%rsp)
movslq 0xcc(%rsp), %rcx
leaq 0xd0(%rsp), %rax
imulq $0x18, %rcx, %rcx
addq %rcx, %rax
movq %rax, 0x10(%rsp)
movq 0x1c0(%rsp), %rdi
callq 0x189b0
cvtss2sd %xmm0, %xmm0
movsd %xmm0, 0x20(%rsp)
movq 0x1c0(%rsp), %rdi
callq 0x189d0
cvtss2sd %xmm0, %xmm0
movsd %xmm0, 0x18(%rsp)
movq 0x1c0(%rsp), %rdi
callq 0x189e0
movq 0x10(%rsp), %rdi
movsd 0x18(%rsp), %xmm1
movaps %xmm0, %xmm2
movsd 0x20(%rsp), %xmm0
cvtss2sd %xmm2, %xmm2
callq 0x50000
movsd 0xc0(%rsp), %xmm0
callq 0x13a30
mulsd 0x170(%rsp), %xmm0
leaq 0xa8(%rsp), %rdi
leaq 0x190(%rsp), %rsi
callq 0x31980
movslq 0xcc(%rsp), %rax
leaq 0xd0(%rsp), %rdi
imulq $0x18, %rax, %rax
addq %rax, %rdi
leaq 0xa8(%rsp), %rsi
callq 0xf8a90
movsd 0xc0(%rsp), %xmm0
callq 0x13110
mulsd 0x168(%rsp), %xmm0
leaq 0x90(%rsp), %rdi
leaq 0x178(%rsp), %rsi
callq 0x31980
movslq 0xcc(%rsp), %rax
leaq 0xd0(%rsp), %rdi
imulq $0x18, %rax, %rax
addq %rax, %rdi
leaq 0x90(%rsp), %rsi
callq 0xf8a90
movl 0xcc(%rsp), %eax
addl $0x1, %eax
movl %eax, 0xcc(%rsp)
jmp 0x4c93b
movl $0x0, 0x8c(%rsp)
cmpl $0x6, 0x8c(%rsp)
jge 0x4cb22
movq 0x1c0(%rsp), %rdi
callq 0x4feb0
movq %rax, %rdi
callq 0x50040
movslq 0x8c(%rsp), %rax
leaq 0xd0(%rsp), %rdi
imulq $0x18, %rax, %rax
addq %rax, %rdi
callq 0x50050
movl 0x8c(%rsp), %eax
addl $0x1, %eax
movl $0x6, %ecx
cltd
idivl %ecx
movslq %edx, %rax
leaq 0xd0(%rsp), %rdi
imulq $0x18, %rax, %rax
addq %rax, %rdi
callq 0x50050
movl 0x8c(%rsp), %eax
addl $0x1, %eax
movl %eax, 0x8c(%rsp)
jmp 0x4caab
jmp 0x4cb24
movl 0x204(%rsp), %eax
addl 0x1c8(%rsp), %eax
movl %eax, 0x1c8(%rsp)
jmp 0x4c706
callq 0x50070
jmp 0x4cc62
xorl %edi, %edi
callq 0x4ff50
movl $0x0, 0x88(%rsp)
movq 0x78(%rsp), %rdi
movl 0x88(%rsp), %eax
movl %eax, 0xc(%rsp)
callq 0x1c0e0
movl %eax, %ecx
movl 0xc(%rsp), %eax
cmpl %ecx, %eax
jge 0x4cc5d
movq 0x78(%rsp), %rax
movq (%rax), %rax
movslq 0x88(%rsp), %rcx
imulq $0x30, %rcx, %rcx
addq %rcx, %rax
movq %rax, 0x80(%rsp)
cmpl $0x0, 0x200(%rsp)
je 0x4cbbb
movq 0x80(%rsp), %rdi
callq 0x4ff80
movq %rax, %rdi
callq 0x4ff70
cmpl $0x0, 0x1e4(%rsp)
je 0x4cbdb
movq 0x80(%rsp), %rdi
callq 0x31f20
movl %eax, %edi
callq 0x4ff90
jmp 0x4cc2e
cmpl $0x0, 0x1f4(%rsp)
je 0x4cc2c
movq 0x80(%rsp), %rdi
callq 0x31fb0
movss %xmm0, 0x8(%rsp)
movq 0x80(%rsp), %rdi
callq 0x31fd0
movss %xmm0, 0x4(%rsp)
movq 0x80(%rsp), %rdi
callq 0x31ff0
movss 0x4(%rsp), %xmm1
movaps %xmm0, %xmm2
movss 0x8(%rsp), %xmm0
callq 0x50080
jmp 0x4cc2e
movq 0x80(%rsp), %rdi
callq 0x4feb0
movq %rax, %rdi
callq 0x50040
movl 0x204(%rsp), %eax
addl 0x88(%rsp), %eax
movl %eax, 0x88(%rsp)
jmp 0x4cb5a
callq 0x50070
callq 0x13a20
addq $0x218, %rsp            # imm = 0x218
retq
nop