Draco Bitstream Specification

Version 2,2
Released 2017-10-25

Frank Galligan, Google

Last modified: 2020-07-07 09:05:20 -0700

Abstract

This document defines the bitstream format and decoding process for the Draco 3D Data Compression scheme. Contributing authors should consult the document’s README file.

Scope

This document specifies the open-source Draco 3D Data Compression bitstream format and decoding process.

Conventions

Draco File Format

  • All Draco encoded mesh files are comprised of four main sections. This first section is the header. The second section contains the metadata. This section is optional. The third section contains the connectivity data. The fourth section contains the attribute data.
Figure 1. Draco file format.
  • The header must be decoded first, then the metadata section (if present), then the connectivity section, and then the attribute section.

Sequential Connectivity

  • The sequential connectivity is comprised of two sections. The first section is the connectivity header. The second section is the indices data.
Figure 2. Sequential connectivity format.

EdgeBreaker Connectivity

  • The EdgeBreaker connectivity section is composed of five sections. The first section is the connectivity header. The second section is the encoded split data. The third section is the encoded EdgeBreaker symbol data. The fourth section is the encoded start face configuration data. The fifth section is the attribute connectivity data.
Figure 3. EdgeBreaker connectivity format.

Valence EdgeBreaker Connectivity

  • The valence EdgeBreaker connectivity adds two sections after the attribute connectivity data. The first additional section is the EdgeBreaker valence header. The second additional section is the context data for the valence prediction.
Figure 4. Valence EdgeBreaker connectivity format.

Attributes

  • The attributes data contains two sections. The first section is the attribute header. The second section is comprised of one or more attribute types, such as positions, texture coordinates, normals… Each attribute type section is comprised of one or more unique attributes.
Figure 5. Attribute data format.

Draco Conventions

  • f[n]
    • Unsigned n-bit number appearing directly in the bitstream. The bits are read from high to low order.
    • When bit reading is finished it will always pad the read to the current byte. ResetBitReader() will signify when the bit reading is finished.
  • I16, UI16, I32, UI32, I64, UI64, and Float values must be little endian.

  • Float is IEEE 754 Single precision.

  • varUI32 and varUI64 types must be decoded by the LEB128() function.

  • varUI32 can represent values in the range 0 to 2^32 - 1.
    • E.g. 0x10000000 will be stored using 5 bytes.
  • varUI64 can represent values in the range 0 to 2^64 - 1.
    • E.g. 0x100000000000000 will be stored using 9 bytes.
  • Metadata keys per metadata element must be unique.

  • att_metadata_id values must be unique.

  • All values of att_metadata_id must equal a value stored in att_dec_unique_id.

  • When parsing a value, that value can be assigned to a variable after the value has been parsed.
    • E.g. sz = buffer_size varUI32
    • buffer UI8[sz]
  • All uninitialized elements of opposite_corners_ shall be set to kInvalidCornerIndex.

General Conventions

The mathematical operators and their precedence rules used to describe this Specification are similar to those used in the C programming language.

Assignment of an array is represented using the normal notation A = B and is specified to mean the same as doing both the individual assignments A[ 0 ] = B[ 0 ] and A[ 1 ] = B[ 1 ]. Equality testing of 2 arrays is represented using the notation A == B and is specified to mean the same as (A[ 0 ] == B[ 0 ] && A[ 1 ] == B[ 1 ]). Inequality testing is defined as A != B and is specified to mean the same as (A[ 0 ] != B[ 0 ] || A[ 1 ] != B[ 1 ]).

Unless otherwise noted, array element assignment will increase the size of the array to include the element. Any remaining new elements will be uninitialized.

The functions assign, back, empty, pop_back, push_back, and size behave similarly on arrays as it is defined for c++ std::vector.

When a variable is said to be representable by a signed integer with x bits, it means that the variable is greater than or equal to -(1 << (x-1)), and that the variable is less than or equal to (1 << (x-1))-1.

The function ReadBits(X), reads the next X bits from an array.

Arithmetic operators

   
+ Addition
Subtraction (as a binary operator) or negation (as a unary prefix operator)
* Multiplication
/ Division
a % b Remainder from division of a by b. Both a and b are positive integers.

Logical operators

   
a && b Logical AND operation between a and b
a || b Logical OR operation between a and b
! Logical NOT operation.

Relational operators

   
> Greater than
>= Greater than or equal to
< Less than
<= Less than or equal to
== Equal to
!= Not equal to

Bitwise operators

   
& AND operation
| OR operation
~ Negation operation
a >> b Shift a in 2’s complement binary integer representation format to the right by b bit positions. This operator is only used with b being a non-negative integer. Bits shifted into the MSBs as a result of the right shift have a value equal to the MSB of a prior to the shift operation.
a << b Shift a in 2’s complement binary integer representation format to the left by b bit positions. This operator is only used with b being a non-negative integer. Bits shifted into the LSBs as a result of the left shift have a value equal to 0.

Assignment

   
= Assignment operator
++ Increment, x++ is equivalent to x = x + 1. When this operator is used for an array index, the variable value is obtained before the auto increment operation
-- Decrement, i.e. x-- is equivalent to x = x - 1. When this operator is used for an array index, the variable value is obtained before the auto decrement operation
+= Addition assignment operator, for example x += 3 corresponds to x = x + 3
-= Subtraction assignment operator, for example x -= 3 corresponds to x = x - 3

Mathematical functions

The following mathematical functions (Abs, Min, and Max) are defined as follows:



Method of describing bitstream syntax

Each syntax element is described by its name (using only lower case letters with underscore characters) and a descriptor for its method of coded representation. The decoding process behaves according to the value of the syntax element and to the values of previously decoded syntax elements.

In some cases the syntax tables may use the values of other variables derived from syntax elements values.

Draco Decoder

Decode()

void Decode() {
  ParseHeader();
  if (flags & METADATA_FLAG_MASK)
    DecodeMetadata();
  DecodeConnectivityData();
  DecodeAttributeData();
}

ParseHeader()

ParseHeader() {
  draco_string                                                                        UI8[5]
  major_version                                                                       UI8
  minor_version                                                                       UI8
  encoder_type                                                                        UI8
  encoder_method                                                                      UI8
  flags                                                                               UI16
}

Metadata Decoder

DecodeMetadata()

void DecodeMetadata() {
  ParseMetadataCount();
  for (i = 0; i < num_att_metadata; ++i) {
    ParseAttributeMetadataId(i);
    DecodeMetadataElement(att_metadata[i]);
  }
  DecodeMetadataElement(file_metadata);
}

ParseMetadataCount()

void ParseMetadataCount() {
  num_att_metadata                                                                    varUI32
}

ParseAttributeMetadataId()

void ParseAttributeMetadataId(index) {
  att_metadata_id[index]                                                              varUI32
}

ParseMetadataElement()

void ParseMetadataElement(metadata) {
  metadata.num_entries                                                                varUI32
  for (i = 0; i < metadata.num_entries; ++i) {
    sz = metadata.key_size[i]                                                         UI8
    metadata.key[i]                                                                   I8[sz]
    sz = metadata.value_size[i]                                                       UI8
    metadata.value[i]                                                                 I8[sz]
  }
  metadata.num_sub_metadata                                                           varUI32
}

ParseSubMetadataKey()

void ParseSubMetadataKey(metadata, index) {
  sz = metadata.sub_metadata_key_size[index]                                          UI8
  metadata.sub_metadata_key[index]                                                    I8[sz]
}

DecodeMetadataElement()

void DecodeMetadataElement(metadata) {
  ParseMetadataElement(metadata);
  for (i = 0; i < metadata.num_sub_metadata; ++i) {
    ParseSubMetadataKey(metadata, i);
    DecodeMetadataElement(metadata.sub_metadata[i]);
  }
}

Connectivity Decoder

DecodeConnectivityData()

void DecodeConnectivityData() {
  if (encoder_method == MESH_SEQUENTIAL_ENCODING)
    DecodeSequentialConnectivityData();
  else if (encoder_method == MESH_EDGEBREAKER_ENCODING)
    DecodeEdgebreakerConnectivityData();
}

Sequential Connectivity Decoder

ParseSequentialConnectivityData()

void ParseSequentialConnectivityData() {
  num_faces                                                                           varUI32
  num_points                                                                          varUI32
  connectivity_method                                                                 UI8
}

ParseSequentialIndicesUI8()

void ParseSequentialIndicesUI8() {
  for (i = 0; i < num_faces; ++i) {
    for (j = 0; j < 3; ++j) {
      face_to_vertex[j][i]                                                            UI8
    }
  }
}

ParseSequentialIndicesUI16()

void ParseSequentialIndicesUI16() {
  for (i = 0; i < num_faces; ++i) {
    for (j = 0; j < 3; ++j) {
      face_to_vertex[j][i]                                                            UI16
    }
  }
}

ParseSequentialIndicesVarUI32()

void ParseSequentialIndicesVarUI32() {
  for (i = 0; i < num_faces; ++i) {
    for (j = 0; j < 3; ++j) {
      face_to_vertex[j][i]                                                            varUI32
    }
  }
}

ParseSequentialIndicesUI32()

void ParseSequentialIndicesUI32() {
  for (i = 0; i < num_faces; ++i) {
    for (j = 0; j < 3; ++j) {
      face_to_vertex[j][i]                                                            UI32
    }
  }
}

DecodeSequentialIndices()

void DecodeSequentialIndices() {
  if (num_points < 256) {
    ParseSequentialIndicesUI8();
  } else if (num_points < (1 << 16)) {
    ParseSequentialIndicesUI16();
  } else if (num_points < (1 << 21)) {
    ParseSequentialIndicesVarUI32();
  } else {
    ParseSequentialIndicesUI32();
  }
}

DecodeSequentialCompressedIndices()

void DecodeSequentialCompressedIndices() {
  DecodeSymbols(num_faces * 3, 1, &decoded_symbols);
  last_index_value = 0;
  for (i = 0; i < num_faces; ++i) {
    for (j = 0; j < 3; ++j) {
      encoded_val = decoded_symbols[i * 3 + j];
      index_diff = (encoded_val >> 1);
      if (encoded_val & 1)
        index_diff = -index_diff;
      val = index_diff + last_index_value;
      face_to_vertex[j][i] = val;
      last_index_value = val;
    }
  }
}

DecodeSequentialConnectivityData()

void DecodeSequentialConnectivityData() {
  ParseSequentialConnectivityData();
  if (connectivity_method == SEQUENTIAL_COMPRESSED_INDICES) {
    DecodeSequentialCompressedIndices();
  } else if (connectivity_method == SEQUENTIAL_UNCOMPRESSED_INDICES) {
    DecodeSequentialIndices();
  }
}

EdgeBreaker Decoder

ParseEdgebreakerConnectivityData()

void ParseEdgebreakerConnectivityData() {
  edgebreaker_traversal_type                                                          UI8
  num_encoded_vertices                                                                varUI32
  num_faces                                                                           varUI32
  num_attribute_data                                                                  UI8
  num_encoded_symbols                                                                 varUI32
  num_encoded_split_symbols                                                           varUI32
}

ParseTopologySplitEvents()

void ParseTopologySplitEvents() {
  num_topology_splits                                                                 varUI32
  for (i = 0; i < num_topology_splits; ++i) {
    source_id_delta[i]                                                                varUI32
    split_id_delta[i]                                                                 varUI32
  }
  for (i = 0; i < num_topology_splits; ++i) {
    source_edge_bit[i]                                                                f[1]
  }
  ResetBitReader();
}

DecodeEdgebreakerConnectivityData()

void DecodeEdgebreakerConnectivityData() {
  curr_att_dec = 0;
  curr_att = 0;
  ParseEdgebreakerConnectivityData();
  DecodeTopologySplitEvents();
  EdgebreakerTraversalStart();
  DecodeEdgeBreakerConnectivity();
}

GetNumComponents()

int GetNumComponents() {
  decoder_type = seq_att_dec_decoder_type[curr_att_dec][curr_att];
  if (decoder_type == SEQUENTIAL_ATTRIBUTE_ENCODER_NORMALS) {
    prediction_scheme = seq_att_dec_prediction_scheme[curr_att_dec][curr_att];
    if (prediction_scheme == PREDICTION_DIFFERENCE) {
      return 2;
    }
  }
  return att_dec_num_components[curr_att_dec][curr_att];
}

ProcessSplitData()

void ProcessSplitData() {
  last_id = 0;
  for (i = 0; i < source_id_delta.size(); ++i) {
    source_symbol_id[i] = source_id_delta[i] + last_id;
    split_symbol_id[i] = source_symbol_id[i] - split_id_delta[i];
    last_id = source_symbol_id[i];
  }
}

DecodeTopologySplitEvents()

void DecodeTopologySplitEvents() {
  ParseTopologySplitEvents();
  ProcessSplitData();
}

IsTopologySplit()

bool IsTopologySplit(encoder_symbol_id, out_face_edge,
                     out_encoder_split_symbol_id) {
  if (source_symbol_id.back() != encoder_symbol_id)
    return false;
  out_face_edge = source_edge_bit.pop_back();
  out_encoder_split_symbol_id = split_symbol_id.pop_back();
  source_symbol_id.pop_back();
  return true;
}

ReplaceVerts()

void ReplaceVerts(from, to) {
  for (i = 0; i < face_to_vertex[0].size(); ++i) {
    if (face_to_vertex[0][i] == from) {
      face_to_vertex[0][i] = to;
    }
    if (face_to_vertex[1][i] == from) {
      face_to_vertex[1][i] = to;
    }
    if (face_to_vertex[2][i] == from) {
      face_to_vertex[2][i] = to;
    }
  }
}

UpdateCornersAfterMerge()

void UpdateCornersAfterMerge(c, v) {
  opp_corner = PosOpposite(c);
  if (opp_corner >= 0) {
    corner_n = Next(opp_corner);
    while (corner_n >= 0) {
      MapCornerToVertex(corner_n, v);
      corner_n = SwingLeft(0, corner_n);
    }
  }
}

NewActiveCornerReached()

void NewActiveCornerReached(new_corner, symbol_id) {
  check_topology_split = false;
  switch (last_symbol_) {
    case TOPOLOGY_C:
      {
        corner_a = active_corner_stack.back();
        corner_b = Previous(corner_a);
        while (PosOpposite(corner_b) >= 0) {
          b_opp = PosOpposite(corner_b);
          corner_b = Previous(b_opp);
        }
        SetOppositeCorners(corner_a, new_corner + 1);
        SetOppositeCorners(corner_b, new_corner + 2);
        active_corner_stack.back() = new_corner;
      }
      vert = CornerToVert(curr_att_dec, Next(corner_a));
      next = CornerToVert(curr_att_dec, Next(corner_b));
      prev = CornerToVert(curr_att_dec, Previous(corner_a));
      if (edgebreaker_traversal_type == VALENCE_EDGEBREAKER) {
        vertex_valences_[next] += 1;
        vertex_valences_[prev] += 1;
      }
      face_to_vertex[0].push_back(vert);
      face_to_vertex[1].push_back(next);
      face_to_vertex[2].push_back(prev);
      is_vert_hole_[vert] = false;
      MapCornerToVertex(new_corner, vert);
      MapCornerToVertex(new_corner + 1, next);
      MapCornerToVertex(new_corner + 2, prev);
      break;
    case TOPOLOGY_S:
      {
        corner_b = active_corner_stack.pop_back();
        for (i = 0; i < topology_split_id.size(); ++i) {
          if (topology_split_id[i] == symbol_id) {
            active_corner_stack.push_back(split_active_corners[i]);
          }
        }
        corner_a = active_corner_stack.back();
        SetOppositeCorners(corner_a, new_corner + 2);
        SetOppositeCorners(corner_b, new_corner + 1);
        active_corner_stack.back() = new_corner;
      }

      vert = CornerToVert(curr_att_dec, Previous(corner_a));
      next = CornerToVert(curr_att_dec, Next(corner_a));
      prev = CornerToVert(curr_att_dec, Previous(corner_b));
      MapCornerToVertex(new_corner, vert);
      MapCornerToVertex(new_corner + 1, next);
      MapCornerToVertex(new_corner + 2, prev);
      corner_n = Next(corner_b);
      vertex_n = CornerToVert(curr_att_dec, corner_n);
      if (edgebreaker_traversal_type == VALENCE_EDGEBREAKER) {
        vertex_valences_[vert] += vertex_valences_[vertex_n];
      }
      ReplaceVerts(vertex_n, vert);
      if (edgebreaker_traversal_type == VALENCE_EDGEBREAKER) {
        vertex_valences_[next] += 1;
        vertex_valences_[prev] += 1;
      }
      face_to_vertex[0].push_back(vert);
      face_to_vertex[1].push_back(next);
      face_to_vertex[2].push_back(prev);
      UpdateCornersAfterMerge(new_corner + 1, vert);
      vertex_corners_[vertex_n] = kInvalidCornerIndex;
      break;
    case TOPOLOGY_R:
      {
        corner_a = active_corner_stack.back();
        opp_corner = new_corner + 2;
        SetOppositeCorners(opp_corner, corner_a);
        active_corner_stack.back() = new_corner;
      }
      check_topology_split = true;
      vert = CornerToVert(curr_att_dec, Previous(corner_a));
      next = CornerToVert(curr_att_dec, Next(corner_a));
      prev = ++last_vert_added;
      if (edgebreaker_traversal_type == VALENCE_EDGEBREAKER) {
        vertex_valences_[vert] += 1;
        vertex_valences_[next] += 1;
        vertex_valences_[prev] += 2;
      }

      face_to_vertex[0].push_back(vert);
      face_to_vertex[1].push_back(next);
      face_to_vertex[2].push_back(prev);

      MapCornerToVertex(new_corner + 2, prev);
      MapCornerToVertex(new_corner, vert);
      MapCornerToVertex(new_corner + 1, next);
      break;
    case TOPOLOGY_L:
      {
        corner_a = active_corner_stack.back();
        opp_corner = new_corner + 1;
        SetOppositeCorners(opp_corner, corner_a);
        active_corner_stack.back() = new_corner;
      }
      check_topology_split = true;
      vert = CornerToVert(curr_att_dec, Next(corner_a));
      next = ++last_vert_added;
      prev = CornerToVert(curr_att_dec, Previous(corner_a));
      if (edgebreaker_traversal_type == VALENCE_EDGEBREAKER) {
        vertex_valences_[vert] += 1;
        vertex_valences_[next] += 2;
        vertex_valences_[prev] += 1;
      }

      face_to_vertex[0].push_back(vert);
      face_to_vertex[1].push_back(next);
      face_to_vertex[2].push_back(prev);

      MapCornerToVertex(new_corner + 2, prev);
      MapCornerToVertex(new_corner, vert);
      MapCornerToVertex(new_corner + 1, next);
      break;
    case TOPOLOGY_E:
      active_corner_stack.push_back(new_corner);
      check_topology_split = true;
      vert = last_vert_added + 1;
      next = vert + 1;
      prev = next + 1;
      if (edgebreaker_traversal_type == VALENCE_EDGEBREAKER) {
        vertex_valences_[vert] += 2;
        vertex_valences_[next] += 2;
        vertex_valences_[prev] += 2;
      }
      face_to_vertex[0].push_back(vert);
      face_to_vertex[1].push_back(next);
      face_to_vertex[2].push_back(prev);
      last_vert_added = prev;
      MapCornerToVertex(new_corner, vert);
      MapCornerToVertex(new_corner + 1, next);
      MapCornerToVertex(new_corner + 2, prev);
      break;
  }

  if (edgebreaker_traversal_type == VALENCE_EDGEBREAKER) {
    // Compute the new context that is going to be used
    // to decode the next symbol.
    active_valence = vertex_valences_[next];
    if (active_valence < MIN_VALENCE) {
      clamped_valence = MIN_VALENCE;
    } else if (active_valence > MAX_VALENCE) {
      clamped_valence = MAX_VALENCE;
    } else {
      clamped_valence = active_valence;
    }
    active_context_ = (clamped_valence - MIN_VALENCE);
  }

  if (check_topology_split) {
    encoder_symbol_id = num_encoded_symbols - symbol_id - 1;
    while (IsTopologySplit(encoder_symbol_id, &split_edge,
                           &enc_split_id)) {
      act_top_corner = active_corner_stack.back();
      if (split_edge == RIGHT_FACE_EDGE) {
        new_active_corner = Next(act_top_corner);
      } else {
        new_active_corner = Previous(act_top_corner);
      }
      // Convert the encoder split symbol id to decoder symbol id.
      dec_split_id = num_encoded_symbols - enc_split_id - 1;
      topology_split_id.push_back(dec_split_id);
      split_active_corners.push_back(new_active_corner);
    }
  }
}

ParseEdgebreakerStandardSymbol()

void ParseEdgebreakerStandardSymbol() {
  symbol = eb_symbol_buffer.ReadBits(1);
  if (symbol != TOPOLOGY_C) {
    // Else decode two additional bits.
    symbol_suffix = eb_symbol_buffer.ReadBits(2);
    symbol |= (symbol_suffix << 1);
  }
  last_symbol_ = symbol;
}

EdgebreakerDecodeSymbol()

void EdgebreakerDecodeSymbol() {
  if (edgebreaker_traversal_type == VALENCE_EDGEBREAKER) {
    EdgebreakerValenceDecodeSymbol();
  } else if (edgebreaker_traversal_type == STANDARD_EDGEBREAKER) {
    ParseEdgebreakerStandardSymbol();
  }
}

DecodeEdgeBreakerConnectivity()

void DecodeEdgeBreakerConnectivity() {
  is_vert_hole_.assign(num_encoded_vertices + num_encoded_split_symbols, true);
  last_vert_added = -1;
  for (i = 0; i < num_encoded_symbols; ++i) {
    EdgebreakerDecodeSymbol();
    corner = 3 * i;
    NewActiveCornerReached(corner, i);
  }
  ProcessInteriorEdges();
}

ProcessInteriorEdges()

void ProcessInteriorEdges() {
  RansInitDecoder(ans_decoder_, eb_start_face_buffer,
      eb_start_face_buffer_size, L_RANS_BASE);

  while (active_corner_stack.size() > 0) {
    corner_a = active_corner_stack.pop_back();
    RabsDescRead(ans_decoder_,
        eb_start_face_buffer_prob_zero, &interior_face);
    if (interior_face) {
      corner_b = Previous(corner_a);
      while (PosOpposite(corner_b) >= 0) {
        b_opp = PosOpposite(corner_b);
        corner_b = Previous(b_opp);
      }
      corner_c = Next(corner_a);
      while (PosOpposite(corner_c) >= 0) {
        c_opp = PosOpposite(corner_c);
        corner_c = Next(c_opp);
      }
      new_corner = face_to_vertex[0].size() * 3;
      SetOppositeCorners(new_corner, corner_a);
      SetOppositeCorners(new_corner + 1, corner_b);
      SetOppositeCorners(new_corner + 2, corner_c);

      CornerToVerts(0, corner_a, &temp_v, &next_a, &temp_p);
      CornerToVerts(0, corner_b, &temp_v, &next_b, &temp_p);
      CornerToVerts(0, corner_c, &temp_v, &next_c, &temp_p);
      MapCornerToVertex(new_corner, next_b);
      MapCornerToVertex(new_corner + 1, next_c);
      MapCornerToVertex(new_corner + 2, next_a);
      face_to_vertex[0].push_back(next_b);
      face_to_vertex[1].push_back(next_c);
      face_to_vertex[2].push_back(next_a);

      // Mark all three vertices as interior.
      is_vert_hole_[next_b] = false;
      is_vert_hole_[next_c] = false;
      is_vert_hole_[next_a] = false;
    }
  }
}

EdgeBreaker Traversal

ParseEdgebreakerTraversalStandardSymbolData()

void ParseEdgebreakerTraversalStandardSymbolData() {
  sz = eb_symbol_buffer_size                                                          varUI64
  eb_symbol_buffer                                                                    UI8[sz]
}

ParseEdgebreakerTraversalStandardFaceData()

void ParseEdgebreakerTraversalStandardFaceData() {
  eb_start_face_buffer_prob_zero                                                      UI8
  sz = eb_start_face_buffer_size                                                      varUI32
  eb_start_face_buffer                                                                UI8[sz]
}

ParseEdgebreakerTraversalStandardAttributeConnectivityData()

void ParseEdgebreakerTraversalStandardAttributeConnectivityData() {
  for (i = 0; i < num_attribute_data; ++i) {
    attribute_connectivity_decoders_prob_zero[i]                                      UI8
    sz = attribute_connectivity_decoders_size[i]                                      varUI32
    attribute_connectivity_decoders_buffer[i]                                         UI8[sz]
  }
}

DecodeEdgebreakerTraversalStandardData()

void DecodeEdgebreakerTraversalStandardData() {
  ParseEdgebreakerTraversalStandardSymbolData()
  ParseEdgebreakerTraversalStandardFaceData()
  ParseEdgebreakerTraversalStandardAttributeConnectivityData()
}

EdgebreakerTraversalStart()

void EdgebreakerTraversalStart() {
  last_symbol_ = -1;
  active_context_ = -1;
  if (edgebreaker_traversal_type == STANDARD_EDGEBREAKER) {
    DecodeEdgebreakerTraversalStandardData();
  } else if (edgebreaker_traversal_type == VALENCE_EDGEBREAKER) {
    EdgeBreakerTraversalValenceStart();
  }
}

IsFaceVisited()

bool IsFaceVisited(face_id) {
  if (face_id < 0)
    return true;  // Invalid faces are always considered as visited.
  return is_face_visited_[face_id];
}

OnNewVertexVisited()

void OnNewVertexVisited(vertex, corner) {
  encoded_attribute_value_index_to_corner_map[curr_att_dec].push_back(corner);
  vertex_to_encoded_attribute_value_index_map[curr_att_dec][vertex] =
      vertex_visited_point_ids[curr_att_dec];
  vertex_visited_point_ids[curr_att_dec]++;
}

EdgeBreakerTraverser_ProcessCorner()

void EdgeBreakerTraverser_ProcessCorner(corner_id) {
  face = corner_id / 3;
  if (IsFaceVisited(face))
    return;  // Already traversed.
  corner_traversal_stack_.push_back(corner_id);
  next_vert = face_to_vertex[1][face];
  prev_vert = face_to_vertex[2][face];
  if (!is_vertex_visited_[next_vert]) {
    is_vertex_visited_[next_vert] = true;
    next_c = Next(corner_id);
    OnNewVertexVisited(next_vert, next_c);
  }
  if (!is_vertex_visited_[prev_vert]) {
    is_vertex_visited_[prev_vert] = true;
    prev_c = Previous(corner_id);
    OnNewVertexVisited(prev_vert, prev_c);
  }
  while (!corner_traversal_stack_.empty()) {
    corner_id = corner_traversal_stack_.back();
    face_id = corner_id / 3;
    if (corner_id < 0 || IsFaceVisited(face_id)) {
      // This face has been already traversed.
      corner_traversal_stack_.pop_back();
      continue;
    }
    while (true) {
      face_id = corner_id / 3;
      is_face_visited_[face_id] = true;
      vert_id = CornerToVert(0, corner_id);
      if (!is_vertex_visited_[vert_id]) {
        on_boundary = IsOnPositionBoundary(vert_id);
        is_vertex_visited_[vert_id] = true;
        OnNewVertexVisited(vert_id, corner_id);
        if (!on_boundary) {
          corner_id = GetRightCorner(corner_id);
          continue;
        }
      }
      right_corner_id = GetRightCorner(corner_id);
      left_corner_id = GetLeftCorner(corner_id);
      right_face_id = right_corner_id < 0 ? -1 : right_corner_id / 3;
      left_face_id = left_corner_id < 0 ? -1 : left_corner_id / 3;
      if (IsFaceVisited(right_face_id)) {
        if (IsFaceVisited(left_face_id)) {
          corner_traversal_stack_.pop_back();
          break;
        } else {
          corner_id = left_corner_id;
        }
      } else {
        if (IsFaceVisited(left_face_id)) {
          corner_id = right_corner_id;
        } else {
          corner_traversal_stack_.back() = left_corner_id;
          corner_traversal_stack_.push_back(right_corner_id);
          break;
        }
      }
    }
  }
}

EdgeBreakerAttributeTraverser_ProcessCorner()

void EdgeBreakerAttributeTraverser_ProcessCorner(corner_id) {
  face = corner_id / 3;
  if (IsFaceVisited(face))
    return;  // Already traversed.
  corner_traversal_stack_.push_back(corner_id);
  CornerToVerts(curr_att_dec, corner_id, &vert_id, &next_vert, &prev_vert);
  if (!is_vertex_visited_[next_vert]) {
    is_vertex_visited_[next_vert] = true;
    next_c = Next(corner_id);
    OnNewVertexVisited(next_vert, next_c);
  }
  if (!is_vertex_visited_[prev_vert]) {
    is_vertex_visited_[prev_vert] = true;
    prev_c = Previous(corner_id);
    OnNewVertexVisited(prev_vert, prev_c);
  }
  while (!corner_traversal_stack_.empty()) {
    corner_id = corner_traversal_stack_.back();
    face_id = corner_id / 3;
    if (corner_id < 0 || IsFaceVisited(face_id)) {
      corner_traversal_stack_.pop_back();
      continue;
    }
    while (true) {
      face_id = corner_id / 3;
      is_face_visited_[face_id] = true;
      vert_id = CornerToVert(curr_att_dec, corner_id);
      if (!is_vertex_visited_[vert_id]) {
        on_seam = IsOnBoundary(curr_att_dec, vert_id);
        pos_vert_id = CornerToVert(0, corner_id);
        on_boundary = (on_seam) ? on_seam : IsOnPositionBoundary(pos_vert_id);
        is_vertex_visited_[vert_id] = true;
        OnNewVertexVisited(vert_id, corner_id);
        if (!on_boundary) {
          corner_id = GetRightCorner(corner_id);
          continue;
        }
      }
      next_c = Next(corner_id);
      right_seam = IsCornerOppositeToSeamEdge(next_c);
      right_corner_id = (right_seam) ? -1 : GetRightCorner(corner_id);
      prev_c = Previous(corner_id);
      left_seam = IsCornerOppositeToSeamEdge(prev_c);
      left_corner_id = (left_seam) ? -1 : GetLeftCorner(corner_id);
      right_face_id = right_corner_id < 0 ? -1 : right_corner_id / 3;
      left_face_id = left_corner_id < 0 ? -1 : left_corner_id / 3;
        if (IsFaceVisited(left_face_id)) {
          corner_traversal_stack_.pop_back();
          break;
        } else {
          corner_id = left_corner_id;
        }
      } else {
        if (IsFaceVisited(left_face_id)) {
          corner_id = right_corner_id;
        } else {
          corner_traversal_stack_.back() = left_corner_id;
          corner_traversal_stack_.push_back(right_corner_id);
          break;
        }
      }
    }
  }
}

EdgeBreaker Traversal Valence

ParseValenceContextCounters()

void ParseValenceContextCounters(index) {
  ebv_context_counters[index]                                                         varUI32
}

EdgeBreakerTraversalValenceStart()

void EdgeBreakerTraversalValenceStart() {
  ParseEdgebreakerTraversalStandardFaceData()
  ParseEdgebreakerTraversalStandardAttributeConnectivityData()
  vertex_valences_.assign(num_encoded_vertices + num_encoded_split_symbols, 0);
  for (i = 0; i < NUM_UNIQUE_VALENCES; ++i) {
    ParseValenceContextCounters(i);
    if (ebv_context_counters[i] > 0) {
      DecodeSymbols(ebv_context_counters[i], 1, &ebv_context_symbols[i]);
    }
  }
}

EdgebreakerValenceDecodeSymbol()

void EdgebreakerValenceDecodeSymbol() {
  if (active_context_ != -1) {
    symbol_id = ebv_context_symbols[active_context_]
                                   [--ebv_context_counters[active_context_]];
    last_symbol_ = edge_breaker_symbol_to_topology_id[symbol_id];
  } else {
    last_symbol_ = TOPOLOGY_E;
  }
}

EdgeBreaker Traversal Prediction Degree

AddCornerToTraversalStack()

void AddCornerToTraversalStack(ci, priority) {
  traversal_stacks_[priority].push_back(ci);
  if (priority < best_priority_)
    best_priority_ = priority;
}

ComputePriority()

int ComputePriority(corner_id) {
  CornerToVerts(curr_att_dec, corner_id, &v_tip, &next_vert, &prev_vert);
  priority = 0;
  if (!is_vertex_visited_[v_tip]) {
    degree = ++prediction_degree_[v_tip];
    priority = (degree > 1 ? 1 : 2);
  }
  if (priority >= kMaxPriority)
    priority = kMaxPriority - 1;
  return priority;
}

PopNextCornerToTraverse()

int PopNextCornerToTraverse() {
  for (i = best_priority_; i < kMaxPriority; ++i) {
    if (!traversal_stacks_[i].empty()) {
      ret = traversal_stacks_[i].pop_back();
      best_priority_ = i;
      return ret;
    }
  }
  return kInvalidCornerIndex;
}

PredictionDegree_TraverseFromCorner()

void PredictionDegree_TraverseFromCorner(corner_id) {
  traversal_stacks_[0].push_back(corner_id);
  best_priority_ = 0;
  CornerToVerts(curr_att_dec, corner_id, &tip_vertex, &next_vert, &prev_vert);
  if (!is_vertex_visited_[next_vert]) {
    is_vertex_visited_[next_vert] = true;
    next_c = Next(corner_id);
    OnNewVertexVisited(next_vert, next_c);
  }
  if (!is_vertex_visited_[prev_vert]) {
    is_vertex_visited_[prev_vert] = true;
    prev_c = Previous(corner_id);
    OnNewVertexVisited(prev_vert, prev_c);
  }
  if (!is_vertex_visited_[tip_vertex]) {
    is_vertex_visited_[tip_vertex] = true;
    OnNewVertexVisited(tip_vertex, corner_id);
  }
  while ((corner_id = PopNextCornerToTraverse()) >= 0) {
    face_id = corner_id / 3;
    if (IsFaceVisited(face_id)) {
      continue;
    }
    while (true) {
      face_id = corner_id / 3;
      is_face_visited_[face_id] = true;
      CornerToVerts(curr_att_dec, corner_id, &vert_id, &next_vert, &prev_vert);
      if (!is_vertex_visited_[vert_id]) {
        is_vertex_visited_[vert_id] = true;
        OnNewVertexVisited(vert_id, corner_id);
      }
      right_corner_id = GetRightCorner(corner_id);
      left_corner_id = GetLeftCorner(corner_id);
      right_face_id = right_corner_id < 0 ? -1 : right_corner_id / 3;
      left_face_id = left_corner_id < 0 ? -1 : left_corner_id / 3;
      is_right_face_visited = IsFaceVisited(right_face_id);
      is_left_face_visited = IsFaceVisited(left_face_id);
      if (!is_left_face_visited) {
        priority = ComputePriority(left_corner_id);
        if (is_right_face_visited && priority <= best_priority_) {
          corner_id = left_corner_id;
          continue;
        } else {
          AddCornerToTraversalStack(left_corner_id, priority);
        }
      }
      if (!is_right_face_visited) {
        priority = ComputePriority(right_corner_id);
        if (priority <= best_priority_) {
          corner_id = right_corner_id;
          continue;
        } else {
          AddCornerToTraversalStack(right_corner_id, priority);
        }
      }
      break;
    }
  }
}

Attributes Decoder

ParseAttributeDecodersData()

void ParseAttributeDecodersData() {
  num_attributes_decoders                                                             UI8
  if (encoder_method == MESH_EDGEBREAKER_ENCODING) {
    for (i = 0; i < num_attributes_decoders; ++i) {
      att_dec_data_id[i]                                                              UI8
      att_dec_decoder_type[i]                                                         UI8
      att_dec_traversal_method[i]                                                     UI8
    }
  }
  for (i = 0; i < num_attributes_decoders; ++i) {
    att_dec_num_attributes[i]                                                         varUI32
    for (j = 0; j < att_dec_num_attributes[i]; ++j) {
      att_dec_att_type[i][j]                                                          UI8
      att_dec_data_type[i][j]                                                         UI8
      att_dec_num_components[i][j]                                                    UI8
      att_dec_normalized[i][j]                                                        UI8
      att_dec_unique_id[i][j]                                                         varUI32
    }
    for (j = 0; j < att_dec_num_attributes[i]; ++j) {
      seq_att_dec_decoder_type[i][j]                                                  UI8
    }
  }
}

DecodeAttributeData()

void DecodeAttributeData() {
  ParseAttributeDecodersData();
  vertex_visited_point_ids.assign(num_attributes_decoders, 0);
  curr_att_dec = 0;
  if (encoder_method == MESH_EDGEBREAKER_ENCODING) {
    DecodeAttributeSeams();
    for (i = 0; i < num_encoded_vertices + num_encoded_split_symbols; ++i) {
      if (is_vert_hole_[i]) {
        UpdateVertexToCornerMap(i);
      }
    }
    for (i = 1; i < num_attributes_decoders; ++i) {
      curr_att_dec = i;
      RecomputeVerticesInternal();
    }
    Attribute_AssignPointsToCorners();
  }
  for (i = 0; i < num_attributes_decoders; ++i) {
    curr_att_dec = i;
    is_face_visited_.assign(num_faces, false);
    is_vertex_visited_.assign(num_faces * 3, false);
    GenerateSequence();
    if (encoder_method == MESH_EDGEBREAKER_ENCODING) {
      UpdatePointToAttributeIndexMapping();
    }
  }
  for (i = 0; i < num_attributes_decoders; ++i) {
    for (j = 0; j < att_dec_num_attributes[i]; ++j) {
      att_dec_num_values_to_decode[i][j] =
          encoded_attribute_value_index_to_corner_map[i].size();
    }
  }
  for (i = 0; i < num_attributes_decoders; ++i) {
    curr_att_dec = i;
    DecodePortableAttributes();
    DecodeDataNeededByPortableTransforms();
    TransformAttributesToOriginalFormat();
  }
}

RecomputeVerticesInternal()

void RecomputeVerticesInternal() {
  attr = curr_att_dec - 1;
  num_new_vertices = 0;
  attr_face_to_vertex.push_back(face_to_vertex);
  corner_to_vertex_map_[curr_att_dec].assign(
      attr_face_to_vertex[attr][0].size() * 3, -1);
  for (v = 0; v < num_encoded_vertices + num_encoded_split_symbols; ++v) {
    c = vertex_corners_[v];
    if (c < 0)
      continue;
    first_vert_id = num_new_vertices++;
    first_c = c;
    if (IsVertexOnAttributeSeam(attr, v)) {
      act_c = SwingLeft(curr_att_dec, first_c);
      while (act_c >= 0) {
        first_c = act_c;
        act_c = SwingLeft(curr_att_dec, act_c);
      }
    }
    corner_to_vertex_map_[curr_att_dec][first_c] = first_vert_id;
    vertex_to_left_most_corner_map_[attr].push_back(first_c);
    act_c = SwingRight(0, first_c);
    while (act_c >= 0 && act_c != first_c) {
      next_act_c = Next(act_c);
      if (IsCornerOppositeToSeamEdge(next_act_c)) {
        first_vert_id = num_new_vertices++;
        vertex_to_left_most_corner_map_[attr].push_back(act_c);
      }
      corner_to_vertex_map_[curr_att_dec][act_c] = first_vert_id;
      act_c = SwingRight(0, act_c);
    }
  }

  for (i = 0; i < corner_to_vertex_map_[curr_att_dec].size(); i += 3) {
    face = i / 3;
    attr_face_to_vertex[attr][0][face] = corner_to_vertex_map_[curr_att_dec][i];
    attr_face_to_vertex[attr][1][face] = corner_to_vertex_map_[curr_att_dec][i + 1];
    attr_face_to_vertex[attr][2][face] = corner_to_vertex_map_[curr_att_dec][i + 2];
  }
}

Attribute_AssignPointsToCorners()

void Attribute_AssignPointsToCorners() {
  point_to_corner_map_count = 0;
  for (v = 0; v < num_encoded_vertices + num_encoded_split_symbols; ++v) {
    c = vertex_corners_[v];
    if (c < 0)
      continue;
    deduplication_first_corner = c;
    if (is_vert_hole_[v]) {
      deduplication_first_corner = c;
    } else {
      for (i = 1; i < num_attributes_decoders; ++i) {
        attr_id = i - 1;
        if (!IsCornerOnAttributeSeam(0, attr_id, c))
          continue;
        vert_id = corner_to_vertex_map_[i][c];
        act_c = SwingRight(0, c);
        seam_found = false;
        while (act_c != c) {
          act_vert_id = corner_to_vertex_map_[i][act_c];
          if (act_vert_id != vert_id) {
            deduplication_first_corner = act_c;
            seam_found = true;
            break;
          }
          act_c = SwingRight(0, act_c);
        }
        if (seam_found)
          break;
      }
    }

    c = deduplication_first_corner;
    corner_to_point_map[c] = point_to_corner_map_count++;
    prev_c = c;
    c = SwingRight(0, c);
    while (c >= 0 && c != deduplication_first_corner) {
      attribute_seam = false;
      for (i = 1; i < num_attributes_decoders; ++i) {
        vert_id = corner_to_vertex_map_[i][c];
        prev_vert_id = corner_to_vertex_map_[i][prev_c];
        if (vert_id != prev_vert_id) {
          attribute_seam = true;
          break;
        }
      }
      if (attribute_seam) {
        corner_to_point_map[c] = point_to_corner_map_count++;
      } else {
        corner_to_point_map[c] = corner_to_point_map[prev_c];
      }
      prev_c = c;
      c = SwingRight(0, c);
    }
  }
}

SequentialGenerateSequence()

void SequentialGenerateSequence() {
  for (i = 0; i < num_points; ++i) {
    encoded_attribute_value_index_to_corner_map[curr_att_dec][i] = i;
  }
}

EdgebreakerGenerateSequence()

void EdgebreakerGenerateSequence() {
  if (att_dec_traversal_method[curr_att_dec] == MESH_TRAVERSAL_PREDICTION_DEGREE) {
    prediction_degree_.assign(num_encoded_vertices + num_encoded_split_symbols, 0);
  }
  for (i = 0; i < num_faces; ++i) {
    if (att_dec_traversal_method[curr_att_dec] == MESH_TRAVERSAL_DEPTH_FIRST) {
      if (curr_att_dec == 0) {
        EdgeBreakerTraverser_ProcessCorner(3 * i);
      } else {
        EdgeBreakerAttributeTraverser_ProcessCorner(3 * i);
      }
    } else {
      PredictionDegree_TraverseFromCorner(3 * i);
    }
  }
}

GenerateSequence()

void GenerateSequence() {
  if (encoder_method == MESH_EDGEBREAKER_ENCODING)
    EdgebreakerGenerateSequence();
  else
    SequentialGenerateSequence();
}

UpdatePointToAttributeIndexMapping()

void UpdatePointToAttributeIndexMapping() {
  indices_map_.assign(num_faces * 3, -1);
  for (f = 0; f < num_faces; ++f) {
    for (p = 0; p < 3; ++p) {
      corner = (f * 3) + p;
      point_id = corner_to_point_map[corner];
      CornerToVerts(curr_att_dec, corner, &vert, &next, &prev);
      att_entry_id =
          vertex_to_encoded_attribute_value_index_map[curr_att_dec][vert];
      indices_map_[point_id] = att_entry_id;
    }
  }
}

TransformAttributesToOriginalFormat_StoreValues()

void TransformAttributesToOriginalFormat_StoreValues() {
  num_components = GetNumComponents();
  num_values = att_dec_num_values_to_decode[curr_att_dec][curr_att];
  portable_attribute_data = seq_int_att_dec_original_values[curr_att_dec][curr_att];
  for (i = 0; i < num_values; ++i) {
    for (c = 0; c < num_components; ++c) {
      out_values.push_back(portable_attribute_data[(i * num_components) + c]);
    }
  }
  seq_int_att_dec_dequantized_values[curr_att_dec][curr_att] = out_values;
}

TransformAttributesToOriginalFormat()

void TransformAttributesToOriginalFormat() {
  for (i = 0; i < att_dec_num_attributes.back(); ++i) {
    curr_att = i;
    dec_type = seq_att_dec_decoder_type[curr_att_dec][curr_att];
    if (dec_type == SEQUENTIAL_ATTRIBUTE_ENCODER_NORMALS) {
      TransformAttributesToOriginalFormat_Normal();
    } else if (dec_type == SEQUENTIAL_ATTRIBUTE_ENCODER_INTEGER) {
      TransformAttributesToOriginalFormat_StoreValues();
    } else {
      SequentialQuantizationAttributeDecoder_DequantizeValues();
    }
  }
}

Sequential Integer Attribute Decoder

ConvertSymbolToSignedInt()

int ConvertSymbolToSignedInt(val) {
  is_positive = !(val & 1);
  val >>= 1;
  if (is_positive) {
    return val;
  }
  val = -val - 1;
  return val;
}

ConvertSymbolsToSignedInts()

void ConvertSymbolsToSignedInts() {
  decoded_symbols = seq_int_att_dec_decoded_values[curr_att_dec][curr_att];
  for (i = 0; i < decoded_symbols.size(); ++i) {
    val = ConvertSymbolToSignedInt(decoded_symbols[i]);
    seq_int_att_dec_symbols_to_signed_ints[i] = val;
  }
}

SequentialIntegerAttributeDecoder_DecodeIntegerValues()

void SequentialIntegerAttributeDecoder_DecodeIntegerValues() {
  num_components = GetNumComponents();
  num_entries = att_dec_num_values_to_decode[curr_att_dec][curr_att];
  num_values = num_entries * num_components;
  if (seq_int_att_dec_compressed[curr_att_dec][curr_att] > 0) {
    DecodeSymbols(num_values, num_components, &decoded_symbols);
  }
  seq_int_att_dec_decoded_values[curr_att_dec][curr_att] = decoded_symbols;
  if (num_values > 0) {
    if (seq_att_dec_prediction_transform_type[curr_att_dec][curr_att] ==
          PREDICTION_TRANSFORM_NORMAL_OCTAHEDRON_CANONICALIZED) {
      decoded_symbols = seq_int_att_dec_decoded_values[curr_att_dec][curr_att];
      for (i = 0; i < decoded_symbols.size(); ++i) {
        signed_vals[i] = decoded_symbols[i];
      }
      seq_int_att_dec_symbols_to_signed_ints[curr_att_dec][curr_att] = signed_vals;
    } else {
      ConvertSymbolsToSignedInts();
    }
  }
  if (seq_att_dec_prediction_scheme[curr_att_dec][curr_att] != PREDICTION_NONE) {
    DecodePredictionData(seq_att_dec_prediction_scheme[curr_att_dec][curr_att]);
    PredictionScheme_ComputeOriginalValues(
        seq_att_dec_prediction_scheme[curr_att_dec][curr_att], num_entries);
  }
}

Boundary Decoder

DecodeAttributeSeams()

void DecodeAttributeSeams() {
  for (a = 0; a < num_attributes_decoders - 1; ++a) {
    RansInitDecoder(ans_decoder_,
        attribute_connectivity_decoders_buffer[a],
        attribute_connectivity_decoders_size[a], L_RANS_BASE);
    ans_decoders.push_back(ans_decoder_);
    is_edge_on_seam_[a].assign(face_to_vertex[0].size() * 3, false);
  }

  for (j = 0; j < num_faces; ++j) {
    face_id = j;
    for (k = 0; k < 3; ++k) {
      local = k;
      corner = (j * 3) + k;
      CornerToVerts(0, corner, v, n, p);
      opp_corner = PosOpposite(corner);
      boundary_edge = opp_corner < 0;
      if (!boundary_edge) {
        if (opp_corner >= corner) {
          for (a = 0; a < num_attributes_decoders - 1; ++a) {
            RabsDescRead(ans_decoders[a],
                         attribute_connectivity_decoders_prob_zero[a], &val);
            if (val) {
              att_connectivity_seam_opp[a].push_back(v);
              att_connectivity_seam_src[a].push_back(n);
              att_connectivity_seam_dest[a].push_back(p);
              is_edge_on_seam_[a][corner] = true;
              if (opp_corner >= 0) {
                CornerToVerts(curr_att_dec, opp_corner, &opp_v, &opp_n, &opp_p);
                att_connectivity_seam_opp[a].push_back(opp_v);
                att_connectivity_seam_src[a].push_back(opp_n);
                att_connectivity_seam_dest[a].push_back(opp_p);
                is_edge_on_seam_[a][opp_corner] = true;
              }
            }
          }
        }
      } else {
        for (a = 0; a < num_attributes_decoders - 1; ++a) {
          att_connectivity_seam_opp[a].push_back(v);
          att_connectivity_seam_src[a].push_back(n);
          att_connectivity_seam_dest[a].push_back(p);
          is_edge_on_seam_[a][corner] = true;
        }
      }
    }
  }
}

IsVertexOnAttributeSeam()

bool IsVertexOnAttributeSeam(attr, vert) {
  for (i = 0; i < att_connectivity_seam_src[attr].size(); ++i) {
    if (att_connectivity_seam_src[attr][i] == vert ||
        att_connectivity_seam_dest[attr][i] == vert) {
      return true;
    }
  }
  return false;
}

IsCornerOnSeam()

bool IsCornerOnSeam(corner) {
  CornerToVerts(0, corner, &v, &n, &p);
  return IsVertexOnAttributeSeam(curr_att_dec - 1, v);
}

IsCornerOnAttributeSeam()

bool IsCornerOnAttributeSeam(att_dec, attr, corner) {
  CornerToVerts(att_dec, corner, &v, &n, &p);
  return IsVertexOnAttributeSeam(attr, v);
}

IsCornerOppositeToSeamEdge()

bool IsCornerOppositeToSeamEdge(corner) {
  attr = curr_att_dec - 1;
  return is_edge_on_seam_[attr][corner];
}

IsOnPositionBoundary()

bool IsOnPositionBoundary(vert_id) {
  if (vertex_corners_[vert_id] < 0)
    return true;
  if (att_dec_decoder_type[curr_att_dec] == MESH_VERTEX_ATTRIBUTE)
    return IsCornerOnAttributeSeam(curr_att_dec, curr_att_dec - 1,
                                   vertex_corners_[vert_id]);
  return false;
}

IsOnAttributeBoundary()

bool IsOnAttributeBoundary(vert) {
  corner = vertex_to_left_most_corner_map_[curr_att_dec - 1][vert];
  if (corner < 0)
    return true;
  return IsCornerOnSeam(corner);
}

IsOnBoundary()

bool IsOnBoundary(att_dec, vert_id) {
  if (att_dec == 0 || att_dec_decoder_type[att_dec] == MESH_VERTEX_ATTRIBUTE)
    return IsOnPositionBoundary(vert_id);
  else
    return IsOnAttributeBoundary(vert_id);
}

Prediction Decoder

ParsePredictionData()

void ParsePredictionData() {
  seq_att_dec_prediction_scheme[curr_att_dec][curr_att]                               I8
  if (seq_att_dec_prediction_scheme[curr_att_dec][curr_att] != PREDICTION_NONE) {
    seq_att_dec_prediction_transform_type[curr_att_dec][curr_att]                     I8
    seq_int_att_dec_compressed[curr_att_dec][curr_att]                                UI8
  }
}

DecodePortableAttributes()

void DecodePortableAttributes() {
  for (i = 0; i < att_dec_num_attributes.back(); ++i) {
    curr_att = i;
    ParsePredictionData();
    if (seq_att_dec_prediction_scheme[curr_att_dec][i] != PREDICTION_NONE) {
      SequentialIntegerAttributeDecoder_DecodeIntegerValues();
    }
  }
}

DecodeDataNeededByPortableTransforms()

void DecodeDataNeededByPortableTransforms() {
  for (i = 0; i < att_dec_num_attributes.back(); ++i) {
    curr_att = i;
    if (seq_att_dec_decoder_type[curr_att_dec][curr_att] ==
        SEQUENTIAL_ATTRIBUTE_ENCODER_NORMALS) {
      ParseQuantizationBits();
    } else if (seq_att_dec_decoder_type[curr_att_dec][curr_att] ==
               SEQUENTIAL_ATTRIBUTE_ENCODER_QUANTIZATION) {
      ParseQuantizationData();
    }
  }
}

ParseWrapTransformData()

void ParseWrapTransformData() {
  pred_trasnform_wrap_min[curr_att_dec][curr_att]                                     I32
  pred_trasnform_wrap_max[curr_att_dec][curr_att]                                     I32
}

ParseNormalOctahedronCanonicalizedTransformData()

void ParseNormalOctahedronCanonicalizedTransformData() {
  pred_trasnform_normal_max_q_val[curr_att_dec][curr_att]                             I32
  unused_center_value                                                                 I32
}

DecodeTransformData()

void DecodeTransformData() {
  transform_type = seq_att_dec_prediction_transform_type[curr_att_dec][curr_att];
  if (transform_type == PREDICTION_TRANSFORM_WRAP) {
    ParseWrapTransformData();
  } else if (transform_type ==
             PREDICTION_TRANSFORM_NORMAL_OCTAHEDRON_CANONICALIZED) {
    ParseNormalOctahedronCanonicalizedTransformData();
  }
}

ParsePredictionRansData()

void ParsePredictionRansData() {
  prediction_rans_prob_zero                                                           UI8
  sz = prediction_rans_data_size                                                      varUI32
  prediction_rans_data_buffer                                                         UI8[sz]
}

ParseConstrainedMultiNumFlags()

void ParseConstrainedMultiNumFlags() {
  constrained_multi_num_flags                                                         varUI32
}

DecodePredictionData_ConstrainedMulti()

void DecodePredictionData_ConstrainedMulti() {
  for (i = 0; i < kMaxNumParallelograms; ++i) {
    ParseConstrainedMultiNumFlags();
    if (constrained_multi_num_flags > 0) {
      ParsePredictionRansData();
      RansInitDecoder(ans_decoder_, prediction_rans_data_buffer,
                      prediction_rans_data_size, L_RANS_BASE);
      for (j = 0; j < constrained_multi_num_flags; ++j) {
        RabsDescRead(ans_decoder_, prediction_rans_prob_zero, &val);
        is_crease_edge_[i][j] = val > 0;
      }
    }
  }
  pred_cons_multi_is_cease_edge[curr_att_dec][curr_att] = is_crease_edge_;
}

ParseTexCoordsNumOrientations()

void ParseTexCoordsNumOrientations() {
  tex_coords_num_orientations                                                         UI32
}

DecodePredictionData_TexCoords()

void DecodePredictionData_TexCoords() {
  ParseTexCoordsNumOrientations();
  ParsePredictionRansData();
  RansInitDecoder(ans_decoder_, prediction_rans_data_buffer,
                  prediction_rans_data_size, L_RANS_BASE);
  last_orientation = true;
  for (i = 0; i < tex_coords_num_orientations; ++i) {
    RabsDescRead(ans_decoder_, prediction_rans_prob_zero, &val);
    if (val == 0)
      last_orientation = !last_orientation;
    orientations.push_back(last_orientation);
  }
  pred_tex_coords_orientations[curr_att_dec][curr_att] = orientations;
}

DecodePredictionData_GeometricNormal()

void DecodePredictionData_GeometricNormal() {
  ParsePredictionRansData();
  RansInitDecoder(ans_decoder_, prediction_rans_data_buffer,
                  prediction_rans_data_size, L_RANS_BASE);
  num_values = att_dec_num_values_to_decode[curr_att_dec][curr_att];
  for (i = 0; i < num_values; ++i) {
    RabsDescRead(ans_decoder_, prediction_rans_prob_zero, &val);
    flip_normal_bits.push_back(val > 0);
  }
  pred_transform_normal_flip_normal_bits[curr_att_dec][curr_att] = flip_normal_bits;
}

DecodePredictionData()

void DecodePredictionData(method) {
  if (method == MESH_PREDICTION_CONSTRAINED_MULTI_PARALLELOGRAM) {
    DecodePredictionData_ConstrainedMulti();
  } else if (method == MESH_PREDICTION_TEX_COORDS_PORTABLE) {
    DecodePredictionData_TexCoords();
  } else if (method == MESH_PREDICTION_GEOMETRIC_NORMAL) {
    DecodeTransformData();
    DecodePredictionData_GeometricNormal();
  }
  if (method != MESH_PREDICTION_GEOMETRIC_NORMAL) {
    DecodeTransformData();
  }
}

PredictionSchemeTransform_ComputeOriginalValue()

void PredictionSchemeTransform_ComputeOriginalValue(pred_vals, corr_vals,
                                                    out_orig_vals) {
  transform_type = eq_att_dec_prediction_transform_type[curr_att_dec][curr_att];
  if (transform_type == PREDICTION_TRANSFORM_WRAP) {
    PredictionSchemeWrapDecodingTransform_ComputeOriginalValue(
        pred_vals, corr_vals, out_orig_vals);
  } else if (transform_type ==
             PREDICTION_TRANSFORM_NORMAL_OCTAHEDRON_CANONICALIZED) {
    PredictionSchemeNormalOctahedronCanonicalizedDecodingTransform_ComputeOriginalValue(
        pred_vals, corr_vals, out_orig_vals);
  }
}

PredictionSchemeDifference_ComputeOriginalValues()

void PredictionSchemeDifference_ComputeOriginalValues(num_values) {
  num_components = GetNumComponents();
  signed_values = seq_int_att_dec_symbols_to_signed_ints[curr_att_dec][curr_att];
  size = num_components * num_values;
  zero_vals.assign(num_components, 0);
  out_values = signed_values;
  PredictionSchemeTransform_ComputeOriginalValue(
      &zero_vals[0], &signed_values[0], &out_values[0]);
  for (i = num_components; i < size; i += num_components) {
    PredictionSchemeTransform_ComputeOriginalValue(
        &out_values[i - num_components], &signed_values[i], &out_values[i]);
  }
  seq_int_att_dec_original_values[curr_att_dec][curr_att] = out_values;
}

PredictionScheme_ComputeOriginalValues()

void PredictionScheme_ComputeOriginalValues(method, num_values) {
  if (method == PREDICTION_DIFFERENCE) {
    PredictionSchemeDifference_ComputeOriginalValues(num_values);
  } else if (method == MESH_PREDICTION_PARALLELOGRAM) {
    MeshPredictionSchemeParallelogramDecoder_ComputeOriginalValues(num_values);
  } else if (method == MESH_PREDICTION_CONSTRAINED_MULTI_PARALLELOGRAM) {
    MeshPredictionSchemeConstrainedMultiParallelogramDecoder_ComputeOriginalValues(
        num_values);
  } else if (method == MESH_PREDICTION_TEX_COORDS_PORTABLE) {
    MeshPredictionSchemeTexCoordsPortableDecoder_ComputeOriginalValues(num_values);
  } else if (method == MESH_PREDICTION_GEOMETRIC_NORMAL) {
    MeshPredictionSchemeGeometricNormalDecoder_ComputeOriginalValues(num_values);
  }
}

Sequential Quantization Attribute Decoder

ParseQuantizationBits()

void ParseQuantizationBits() {
  quantized_data_quantization_bits[curr_att_dec][curr_att]                            UI8
}

ParseQuantizationData()

void ParseQuantizationData() {
  num_components = GetNumComponents();
  for (j = 0; j < num_components; ++j) {
    quantized_data_min_values[curr_att_dec][curr_att][i]                              Float
  }
  quantized_data_max_value_df[curr_att_dec][curr_att]                                 Float
  ParseQuantizationBits();
}

DequantizeFloat()

float DequantizeFloat(val, max_quantized_value_factor_, range_) {
  neg = (val < 0);
  if (neg) {
    val = -val;
  }
  norm_value = val * max_quantized_value_factor_;
  if (neg)
    norm_value = -norm_value;
  return norm_value * range_;
}

SequentialQuantizationAttributeDecoder_DequantizeValues()

void SequentialQuantizationAttributeDecoder_DequantizeValues() {
  quantization_bits = quantized_data_quantization_bits[curr_att_dec][curr_att];
  max_quantized_value = (1 << (quantization_bits)) - 1;
  num_components = GetNumComponents();
  quant_val_id = 0;
  range_ = quantized_data_max_value_df[curr_att_dec][curr_att];
  max_quantized_value_factor_ = 1.f / max_quantized_value;
  min_value_ = quantized_data_min_values[curr_att_dec][curr_att];
  original_values = seq_int_att_dec_original_values[curr_att_dec][curr_att];
  num_values = att_dec_num_values_to_decode[curr_att_dec][curr_att];
  for (i = 0; i < num_values; ++i) {
    for (c = 0; c < num_components; ++c) {
      value = DequantizeFloat(original_values[quant_val_id++],
                              max_quantized_value_factor_, range_);
      value = value + min_value_[c];
      att_val[c] = value;
      dequantized_data.push_back(value);
    }
  }
  seq_int_att_dec_dequantized_values[curr_att_dec][curr_att] = dequantized_data;
}

Sequential Normal Attribute Decoder

MostSignificantBit()

int MostSignificantBit(n) {
  msb = -1;
  while (n != 0) {
    msb++;
    n >>= 1;
  }
  return msb;
}

OctaherdalCoordsToUnitVector()

void OctaherdalCoordsToUnitVector(in_s, in_t, out_vector) {
  s = in_s;
  t = in_t;
  spt = s + t;
  smt = s - t;
  x_sign = 1.0;
  if (spt >= 0.5 && spt <= 1.5 && smt >= -0.5 && smt <= 0.5) {
    // Right hemisphere. Don't do anything.
  } else {
    x_sign = -1.0;
    if (spt <= 0.5) {
      s = 0.5 - in_t;
      t = 0.5 - in_s;
    } else if (spt >= 1.5) {
      s = 1.5 - in_t;
      t = 1.5 - in_s;
    } else if (smt <= -0.5) {
      s = in_t - 0.5;
      t = in_s + 0.5;
    } else {
      s = in_t + 0.5;
      t = in_s - 0.5;
    }
    spt = s + t;
    smt = s - t;
  }
  y = 2.0 * s - 1.0;
  z = 2.0 * t - 1.0;
  x = Min(Min(2.0 * spt - 1.0, 3.0 - 2.0 * spt),
      Min(2.0 * smt + 1.0, 1.0 - 2.0 * smt)) * x_sign;
  normSquared = x * x + y * y + z * z;
  if (normSquared < 1e-6) {
    out_vector[0] = 0;
    out_vector[1] = 0;
    out_vector[2] = 0;
  } else {
    const float d = 1.0 / std::sqrt(normSquared);
    out_vector[0] = x * d;
    out_vector[1] = y * d;
    out_vector[2] = z * d;
  }
}

QuantizedOctaherdalCoordsToUnitVector()

void QuantizedOctaherdalCoordsToUnitVector(in_s, in_t, out_vector) {
  encoded_max_quantized_value =
      pred_trasnform_normal_max_q_val[curr_att_dec][curr_att];
  quantization_bits_ = MostSignificantBit(encoded_max_quantized_value) + 1;
  max_quantized_value_ = (1 << quantization_bits_) - 1;
  max_value_ = max_quantized_value_ - 1;
  scale = 1.0 / max_value_;
  OctaherdalCoordsToUnitVector(in_s * scale, in_t * scale, out_vector);
}

TransformAttributesToOriginalFormat_Normal()

void TransformAttributesToOriginalFormat_Normal() {
  quant_val_id = 0;
  portable_attribute_data = seq_int_att_dec_original_values[curr_att_dec][curr_att];
  num_points = att_dec_num_values_to_decode[curr_att_dec][curr_att];
  for (i = 0; i < num_points; ++i) {
    s = portable_attribute_data[quant_val_id++];
    t = portable_attribute_data[quant_val_id++];
    QuantizedOctaherdalCoordsToUnitVector(s, t, att_val);
    for (j = 0; j < 3; ++j) {
      normals.push_back(att_val[j]);
    }
  }
  seq_int_att_dec_dequantized_values[curr_att_dec][curr_att] = normals;
}

TexCoords Prediction Decoder

IntSqrt()

uint64_t IntSqrt(number) {
  if (number == 0)
    return 0;
  act_number = number;
  square_root = 1;
  while (act_number >= 2) {
    square_root *= 2;
    act_number /= 4;
  }
  do {
    square_root = (square_root + number / square_root) / 2;
  } while (square_root * square_root > number);
  return square_root;
}

GetPositionForEntryId()

void GetPositionForEntryId(entry_id, pos) {
  corner = encoded_attribute_value_index_to_corner_map[curr_att_dec][entry_id];
  point_id = corner_to_point_map[corner];
  mapped_index = indices_map_[0][point_id];
  pos_orig = seq_int_att_dec_original_values[0][0];
  for (i = 0; i < 3; ++i) {
    pos.push_back(pos_orig[(mapped_index * 3) + i]);
  }
}

GetTexCoordForEntryId()

void GetTexCoordForEntryId(entry_id, data, tex_coords) {
  data_offset = entry_id * kTexCoordsNumComponents;
  tex_coords.push_back(data[data_offset]);
  tex_coords.push_back(data[data_offset + 1]);
}

MeshPredictionSchemeTexCoordsPortablePredictor_ComputePredictedValue()

void MeshPredictionSchemeTexCoordsPortablePredictor_ComputePredictedValue(
     corner_id, data, data_id, predicted_value_) {
  CornerToVerts(curr_att_dec, corner_id, &vert_id, &next_vert_id, &prev_vert_id);
  next_data_id =
      vertex_to_encoded_attribute_value_index_map[curr_att_dec][next_vert_id];
  prev_data_id =
      vertex_to_encoded_attribute_value_index_map[curr_att_dec][prev_vert_id];

  if (prev_data_id < data_id && next_data_id < data_id) {
    GetTexCoordForEntryId(next_data_id, data, &n_uv);
    GetTexCoordForEntryId(prev_data_id, data, &p_uv);
    if (p_uv == n_uv) {
      predicted_value_[0] = p_uv[0];
      predicted_value_[1] = p_uv[1];
      return;
    }
    GetPositionForEntryId(data_id, &tip_pos);
    GetPositionForEntryId(next_data_id, &next_pos);
    GetPositionForEntryId(prev_data_id, &prev_pos);
    SubtractVectors(prev_pos, next_pos, &pn);
    Dot(pn, pn, &pn_norm2_squared);

    if (pn_norm2_squared != 0) {
      SubtractVectors(tip_pos, next_pos, &cn);
      Dot(cn, pn, &cn_dot_pn);
      SubtractVectors(p_uv, n_uv, &pn_uv);
      MultiplyScalar(pn_uv, cn_dot_pn, &vec_mult_1);
      MultiplyScalar(n_uv, pn_norm2_squared, &vec_mult_2);
      AddVectors(vec_mult_1, vec_mult_2, &x_uv);
      MultiplyScalar(pn, cn_dot_pn, &vec_mult);
      DivideScalar(vec_mult, pn_norm2_squared, &vec_div);
      AddVectors(next_pos, vec_div, &x_pos);
      SubtractVectors(tip_pos, x_pos, &vec_sub);
      Dot(vec_sub, vec_sub, &cx_norm2_squared);

      temp_vec.push_back(pn_uv[1]);
      temp_vec.push_back(-pn_uv[0]);
      norm_squared = IntSqrt(cx_norm2_squared * pn_norm2_squared);
      MultiplyScalar(temp_vec, norm_squared, &cx_uv);
      orientation = pred_tex_coords_orientations[curr_att_dec][curr_att].pop_back();
      if (orientation)
        AddVectors(x_uv, cx_uv, &temp_vec);
      else
        SubtractVectors(x_uv, cx_uv, &temp_vec);
      DivideScalar(temp_vec, pn_norm2_squared, &predicted_uv);
      predicted_value_[0] = predicted_uv[0];
      predicted_value_[1] = predicted_uv[1];
      return;
    }
  }
  data_offset = 0;
  if (prev_data_id < data_id) {
    data_offset = prev_data_id * kTexCoordsNumComponents;
  }
  if (next_data_id < data_id) {
    data_offset = next_data_id * kTexCoordsNumComponents;
  } else {
    if (data_id > 0) {
      data_offset = (data_id - 1) * kTexCoordsNumComponents;
    } else {
      for (i = 0; i < kTexCoordsNumComponents; ++i) {
        predicted_value_[i] = 0;
      }
      return;
    }
  }
  for (i = 0; i < kTexCoordsNumComponents; ++i) {
    predicted_value_[i] = data[data_offset + i];
  }
}

MeshPredictionSchemeTexCoordsPortableDecoder_ComputeOriginalValues()

void MeshPredictionSchemeTexCoordsPortableDecoder_ComputeOriginalValues(num_values)
{
  signed_values = seq_int_att_dec_symbols_to_signed_ints[curr_att_dec][curr_att];
  num_components = GetNumComponents();
  corner_map_size = num_values;
  out_values = signed_values;
  for (p = 0; p < corner_map_size; ++p) {
    corner_id = encoded_attribute_value_index_to_corner_map[curr_att_dec][p];
    MeshPredictionSchemeTexCoordsPortablePredictor_ComputePredictedValue(
        corner_id, &out_values[0], p, &predicted_value_);
    dst_offset = p * num_components;
    PredictionSchemeWrapDecodingTransform_ComputeOriginalValue(
        &predicted_value_[0], &out_values[dst_offset], &out_values[dst_offset]);
  }
  seq_int_att_dec_original_values[curr_att_dec][curr_att] = out_values;
}

Normal Prediction Decoder

GetPositionForDataId()

void GetPositionForDataId(data_id, pos) {
  corner = encoded_attribute_value_index_to_corner_map[curr_att_dec][data_id];
  point_id = corner_to_point_map[corner];
  mapped_index = indices_map_[0][point_id];
  pos_orig = seq_int_att_dec_original_values[0][0];
  for (i = 0; i < 3; ++i) {
    pos.push_back(pos_orig[(mapped_index * 3) + i]);
  }
}

GetPositionForCorner()

void GetPositionForCorner(ci, pos) {
  CornerToVerts(curr_att_dec, ci, &vert_id, &n, &p);
  data_id = vertex_to_encoded_attribute_value_index_map[curr_att_dec][vert_id];
  GetPositionForDataId(data_id, pos);
}

MeshPredictionSchemeGeometricNormalPredictorArea_ComputePredictedValue()

void MeshPredictionSchemeGeometricNormalPredictorArea_ComputePredictedValue(
    corner_id, predicted_value_) {
  GetPositionForCorner(corner_id, &pos_cent);
  normal.assign(3, 0);
  corner = corner_id;
  start_corner_ = corner;
  left_traversal_ = true;
  while (corner >= 0) {
    c_next = Next(corner);
    c_prev = Previous(corner);
    GetPositionForCorner(c_next, &pos_next);
    GetPositionForCorner(c_prev, &pos_prev);
    SubtractVectors(pos_next, pos_cent, &delta_next);
    SubtractVectors(pos_prev, pos_cent, &delta_prev);
    CrossProduct(delta_next, delta_prev, &cross);
    AddVectors(normal, cross, &temp_norm);
    for (i = 0; i < temp_norm.size(); ++i) {
      normal[i] = temp_norm[i];
    }
    if (left_traversal_) {
      left_c = SwingLeft(curr_att_dec, corner);
      corner = left_c;
      if (corner < 0) {
        right_c = SwingRight(curr_att_dec, start_corner_);
        corner = right_c;
        left_traversal_ = false;
      } else if (corner == start_corner_) {
        corner = kInvalidCornerIndex;
      }
    } else {
      right_c = SwingRight(curr_att_dec, corner);
      corner = right_c;
    }
  }
  AbsSum(normal, &abs_sum);
  upper_bound = 1 << 29;
  if (abs_sum > upper_bound) {
    quotient = abs_sum / upper_bound;
    DivideScalar(normal, quotient, &vec_div);
    for (i = 0; i < vec_div.size(); ++i) {
      normal[i] = vec_div[i];
    }
  }
  predicted_value_[0] = normal[0];
  predicted_value_[1] = normal[1];
  predicted_value_[2] = normal[2];
}

CanonicalizeIntegerVector()

void CanonicalizeIntegerVector(vec, center_value_) {
  abs_sum = Abs(vec[0]) + Abs(vec[1]) + Abs(vec[2]);
  if (abs_sum == 0) {
    vec[0] = center_value_;
  } else {
    vec[0] = (vec[0] * center_value_) / abs_sum;
    vec[1] = (vec[1] * center_value_) / abs_sum;
    if (vec[2] >= 0) {
      vec[2] = center_value_ - Abs(vec[0]) - Abs(vec[1]);
    } else {
      vec[2] = -(center_value_ - Abs(vec[0]) - Abs(vec[1]));
    }
  }
}

CanonicalizeOctahedralCoords()

void CanonicalizeOctahedralCoords(s, t, out_s,
                                  out_t, center_value_, max_value_) {
  if ((s == 0 && t == 0) || (s == 0 && t == max_value_) ||
      (s == max_value_ && t == 0)) {
    s = max_value_;
    t = max_value_;
  } else if (s == 0 && t > center_value_) {
    t = center_value_ - (t - center_value_);
  } else if (s == max_value_ && t < center_value_) {
    t = center_value_ + (center_value_ - t);
  } else if (t == max_value_ && s < center_value_) {
    s = center_value_ + (center_value_ - s);
  } else if (t == 0 && s > center_value_) {
    s = center_value_ - (s - center_value_);
  }
  out_s = s;
  out_t = t;
}

IntegerVectorToQuantizedOctahedralCoords()

void IntegerVectorToQuantizedOctahedralCoords(
    int_vec, out_s, out_t, center_value_, max_value_) {
  if (int_vec[0] >= 0) {
    s = (int_vec[1] + center_value_);
    t = (int_vec[2] + center_value_);
  } else {
    if (int_vec[1] < 0) {
      s = Abs(int_vec[2]);
    } else {
      s = (max_value_ - Abs(int_vec[2]));
    }
    if (int_vec[2] < 0) {
      t = Abs(int_vec[1]);
    } else {
      t = (max_value_ - Abs(int_vec[1]));
    }
  }
  CanonicalizeOctahedralCoords(s, t, out_s, out_t, center_value_, max_value_);
}

MeshPredictionSchemeGeometricNormalDecoder_ComputeOriginalValues()

void MeshPredictionSchemeGeometricNormalDecoder_ComputeOriginalValues(num_values) {
  signed_values = seq_int_att_dec_symbols_to_signed_ints[curr_att_dec][curr_att];
  encoded_max_quantized_value =
      pred_trasnform_normal_max_q_val[curr_att_dec][curr_att];
  quantization_bits_ = MostSignificantBit(encoded_max_quantized_value) + 1;
  max_quantized_value_ = (1 << quantization_bits_) - 1;
  max_value_ = max_quantized_value_ - 1;
  center_value_ = max_value_ / 2;
  corner_map_size = num_values;
  flip_normal_bits = pred_transform_normal_flip_normal_bits[curr_att_dec][curr_att];
  out_values = signed_values;
  for (data_id = 0; data_id < corner_map_size; ++data_id) {
    corner_id = encoded_attribute_value_index_to_corner_map[curr_att_dec][data_id];
    MeshPredictionSchemeGeometricNormalPredictorArea_ComputePredictedValue(
        corner_id, &pred_normal_3d);
    CanonicalizeIntegerVector(pred_normal_3d, center_value_);
    if (flip_normal_bits[data_id]) {
      for (i = 0; i < pred_normal_3d.size(); ++i) {
        pred_normal_3d[i] = -pred_normal_3d[i];
      }
    }
    IntegerVectorToQuantizedOctahedralCoords(&pred_normal_3d[0],
        &pred_normal_oct[0], &pred_normal_oct[1], center_value_, max_value_);
    data_offset = data_id * 2;
    PredictionSchemeNormalOctahedronCanonicalizedDecodingTransform_ComputeOriginalValue(
         &pred_normal_oct[0], &out_values[data_offset], &out_values[data_offset]);
  }
  seq_int_att_dec_original_values[curr_att_dec][curr_att] = out_values;
}

Prediction Normal Transform

ModMax()

int32_t ModMax(x, center_value_, max_quantized_value_) {
  if (x > center_value_)
    return x - max_quantized_value_;
  if (x < -center_value_)
    return x + max_quantized_value_;
  return x;
}

InvertDiamond()

void InvertDiamond(s, t, center_value_) {
  sign_s = 0;
  sign_t = 0;
  if (s >= 0 && t >= 0) {
    sign_s = 1;
    sign_t = 1;
  } else if (s <= 0 && t <= 0) {
    sign_s = -1;
    sign_t = -1;
  } else {
    sign_s = (s > 0) ? 1 : -1;
    sign_t = (t > 0) ? 1 : -1;
  }
  corner_point_s = sign_s * center_value_;
  corner_point_t = sign_t * center_value_;
  s = 2 * s - corner_point_s;
  t = 2 * t - corner_point_t;
  if (sign_s * sign_t >= 0) {
    temp = s;
    s = -t;
    t = -temp;
  } else {
    temp = s;
    s = t;
    t = temp;
  }
  s = (s + corner_point_s) / 2;
  t = (t + corner_point_t) / 2;
}

GetRotationCount()

void GetRotationCount(pred, count) {
  sign_x = pred[0];
  sign_y = pred[1];
  rotation_count = 0;
  if (sign_x == 0) {
    if (sign_y == 0) {
      rotation_count = 0;
    } else if (sign_y > 0) {
      rotation_count = 3;
    } else {
      rotation_count = 1;
    }
  } else if (sign_x > 0) {
    if (sign_y >= 0) {
      rotation_count = 2;
    } else {
      rotation_count = 1;
    }
  } else {
    if (sign_y <= 0) {
      rotation_count = 0;
    } else {
      rotation_count = 3;
    }
  }
  count = rotation_count;
}

RotatePoint()

void RotatePoint(p, rotation_count, out_p) {
  switch (rotation_count) {
    case 1:
      out_p.push_back(p[1]);
      out_p.push_back(-p[0]);
      return;
    case 2:
      out_p.push_back(-p[0]);
      out_p.push_back(-p[1]);
      return;
    case 3:
      out_p.push_back(-p[1]);
      out_p.push_back(p[0]);
      return;
    default:
      out_p.push_back(p[0]);
      out_p.push_back(p[1]);
      return;
  }
}

IsInBottomLeft()

bool IsInBottomLeft(p) {
  if (p[0] == 0 && p[1] == 0)
    return true;
  return (p[0] < 0 && p[1] <= 0);
}

PredictionSchemeNormalOctahedronCanonicalizedDecodingTransform_ComputeOriginalValue2()

void PredictionSchemeNormalOctahedronCanonicalizedDecodingTransform_ComputeOriginalValue2(
    pred_in, corr, out, center_value_, max_quantized_value_) {
  t.assign(2, center_value_);
  SubtractVectors(pred_in, t, &pred);
  pred_is_in_diamond = Abs(pred[0]) + Abs(pred[1]) <= center_value_;
  if (!pred_is_in_diamond) {
    InvertDiamond(&pred[0], &pred[1], center_value_);
  }
  pred_is_in_bottom_left = IsInBottomLeft(pred);
  GetRotationCount(pred, &rotation_count);
  if (!pred_is_in_bottom_left) {
    RotatePoint(pred, rotation_count, &temp_rot);
    for (i = 0; i < temp_rot.size(); ++i) {
      pred[i] = temp_rot[i];
    }
  }

  AddVectors(pred, corr, &orig);
  orig[0] = ModMax(orig[0], center_value_, max_quantized_value_);
  orig[1] = ModMax(orig[1], center_value_, max_quantized_value_);
  if (!pred_is_in_bottom_left) {
    reverse_rotation_count = (4 - rotation_count) % 4;
    RotatePoint(orig, reverse_rotation_count, &temp_rot);
    for (i = 0; i < temp_rot.size(); ++i) {
      orig[i] = temp_rot[i];
    }
  }
  if (!pred_is_in_diamond) {
    InvertDiamond(&orig[0], &orig[1], center_value_);
  }
  AddVectors(orig, t, out);
}

PredictionSchemeNormalOctahedronCanonicalizedDecodingTransform_ComputeOriginalValue()

void PredictionSchemeNormalOctahedronCanonicalizedDecodingTransform_ComputeOriginalValue(
    pred_vals, corr_vals, out_orig_vals) {
  encoded_max_quantized_value =
      pred_trasnform_normal_max_q_val[curr_att_dec][curr_att];
  quantization_bits_ = MostSignificantBit(encoded_max_quantized_value) + 1;
  max_quantized_value_ = (1 << quantization_bits_) - 1;
  max_value_ = max_quantized_value_ - 1;
  center_value_ = max_value_ / 2;

  pred.push_back(pred_vals[0]);
  pred.push_back(pred_vals[1]);
  corr.push_back(corr_vals[0]);
  corr.push_back(corr_vals[1]);
  PredictionSchemeNormalOctahedronCanonicalizedDecodingTransform_ComputeOriginalValue2(
      pred, corr, &orig, center_value_, max_quantized_value_);
  out_orig_vals[0] = orig[0];
  out_orig_vals[1] = orig[1];
}

Prediction Wrap Transform

PredictionSchemeWrapTransformBase_ClampPredictedValue()

void PredictionSchemeWrapTransformBase_ClampPredictedValue(predicted_val,
                                                           clamped_value_) {
  num_components = GetNumComponents();
  min_value_ = pred_trasnform_wrap_min[curr_att_dec][curr_att];
  max_value_ = pred_trasnform_wrap_max[curr_att_dec][curr_att];
  for (i = 0; i < num_components; ++i) {
    if (predicted_val[i] > max_value_)
      clamped_value_[i] = max_value_;
    else if (predicted_val[i] < min_value_)
      clamped_value_[i] = min_value_;
    else
      clamped_value_[i] = predicted_val[i];
  }
}

PredictionSchemeWrapDecodingTransform_ComputeOriginalValue()

void PredictionSchemeWrapDecodingTransform_ComputeOriginalValue(
    predicted_vals, corr_vals, out_original_vals) {
  num_components = GetNumComponents();
  min = pred_trasnform_wrap_min[curr_att_dec][curr_att];
  max = pred_trasnform_wrap_max[curr_att_dec][curr_att];
  max_dif_ = 1 + max - min;
  PredictionSchemeWrapTransformBase_ClampPredictedValue(predicted_vals,
                                                        clamped_vals);
  for (i = 0; i < num_components; ++i) {
    out_original_vals[i] = clamped_vals[i] + corr_vals[i];
    if (out_original_vals[i] > max)
      out_original_vals[i] -= max_dif_;
    else if (out_original_vals[i] < min)
      out_original_vals[i] += max_dif_;
  }
}

Parallelogram Prediction Decoder

GetParallelogramEntries()

void GetParallelogramEntries(ci, opp_entry,
                             next_entry, prev_entry) {
  CornerToVerts(curr_att_dec, ci, &v, &n, &p);
  opp_entry = vertex_to_encoded_attribute_value_index_map[curr_att_dec][v];
  next_entry = vertex_to_encoded_attribute_value_index_map[curr_att_dec][n];
  prev_entry = vertex_to_encoded_attribute_value_index_map[curr_att_dec][p];
}

ComputeParallelogramPrediction()

bool ComputeParallelogramPrediction(data_entry_id, ci, in_data,
                                    num_components, out_prediction) {
  oci = Opposite(curr_att_dec, ci);
  if (oci < 0)
    return false;
  GetParallelogramEntries(oci, &vert_opp, &vert_next, &vert_prev);
  if (vert_opp < data_entry_id && vert_next < data_entry_id &&
      vert_prev < data_entry_id) {
    v_opp_off = vert_opp * num_components;
    v_next_off = vert_next * num_components;
    v_prev_off = vert_prev * num_components;
    for (c = 0; c < num_components; ++c) {
      out_prediction[c] = (in_data[v_next_off + c] + in_data[v_prev_off + c]) -
          in_data[v_opp_off + c];
    }
    return true;
  }
  return false;
}

MeshPredictionSchemeParallelogramDecoder_ComputeOriginalValues()

void MeshPredictionSchemeParallelogramDecoder_ComputeOriginalValues(num_values) {
  signed_values = seq_int_att_dec_symbols_to_signed_ints[curr_att_dec][curr_att];
  num_components = GetNumComponents();
  pred_vals.assign(num_components, 0);
  out_values = signed_values;
  PredictionSchemeWrapDecodingTransform_ComputeOriginalValue(pred_vals,
      &signed_values[0], &out_values[0]);
  corner_map_size = num_values;
  for (p = 1; p < corner_map_size; ++p) {
    corner_id = encoded_attribute_value_index_to_corner_map[curr_att_dec][p];
    dst_offset = p * num_components;
    if (!ComputeParallelogramPrediction(p, corner_id, &out_values[0],
                                        num_components, pred_vals)) {
      src_offset = (p - 1) * num_components;
      PredictionSchemeWrapDecodingTransform_ComputeOriginalValue(
          &out_values[src_offset], &signed_values[dst_offset],
          &out_values[dst_offset]);
    } else {
      PredictionSchemeWrapDecodingTransform_ComputeOriginalValue(
          pred_vals, &signed_values[dst_offset], &out_values[dst_offset]);
    }
  }
  seq_int_att_dec_original_values[curr_att_dec][curr_att] = out_values;
}

Multi Parallelogram Prediction Decoder

MeshPredictionSchemeConstrainedMultiParallelogramDecoder_ComputeOriginalValues()

void MeshPredictionSchemeConstrainedMultiParallelogramDecoder_ComputeOriginalValues(
      num_values) {
  signed_values = seq_int_att_dec_symbols_to_signed_ints[curr_att_dec][curr_att];
  num_components = GetNumComponents();
  for (i = 0; i < kMaxNumParallelograms; ++i) {
    pred_vals[i].resize(num_components, 0);
  }
  out_values = signed_values;
  PredictionSchemeTransform_ComputeOriginalValue(
      pred_vals[0], &signed_values[0], &out_values[0]);
  is_crease_edge_pos.assign(kMaxNumParallelograms, 0);
  corner_map_size = num_values;
  for (p = 1; p < corner_map_size; ++p) {
    start_corner_id = encoded_attribute_value_index_to_corner_map[curr_att_dec][p];
    corner_id = start_corner_id;
    num_parallelograms = 0;
    first_pass = true;
    while (corner_id >= 0) {
      if (ComputeParallelogramPrediction(p, corner_id, &out_values[0],
          num_components, &(pred_vals[num_parallelograms][0]))) {
        ++num_parallelograms;
        if (num_parallelograms == kMaxNumParallelograms)
          break;
      }
      if (first_pass) {
        corner_id = SwingLeft(curr_att_dec, corner_id);
      } else {
        corner_id = SwingRight(curr_att_dec, corner_id);
      }
      if (corner_id == start_corner_id) {
        break;
      }
      if (corner_id < 0 && first_pass) {
        first_pass = false;
        corner_id = SwingRight(curr_att_dec, start_corner_id);
      }
    }
    is_crease_edge_ = pred_cons_multi_is_cease_edge[curr_att_dec][curr_att];
    num_used_parallelograms = 0;
    if (num_parallelograms > 0) {
      for (i = 0; i < num_components; ++i) {
        multi_pred_vals[i] = 0;
      }
      for (i = 0; i < num_parallelograms; ++i) {
        context = num_parallelograms - 1;
        is_crease = is_crease_edge_[context][is_crease_edge_pos[context]++];
        if (!is_crease) {
          ++num_used_parallelograms;
          for (j = 0; j < num_components; ++j) {
            multi_pred_vals[j] += pred_vals[i][j];
          }
        }
      }
    }
    dst_offset = p * num_components;
    if (num_used_parallelograms == 0) {
      src_offset = (p - 1) * num_components;
      PredictionSchemeTransform_ComputeOriginalValue(&out_values[src_offset],
          &signed_values[dst_offset], &out_values[dst_offset]);
    } else {
      for (c = 0; c < num_components; ++c) {
        multi_pred_vals[c] /= num_used_parallelograms;
      }
      PredictionSchemeTransform_ComputeOriginalValue(multi_pred_vals,
          &signed_values[dst_offset], &out_values[dst_offset]);
    }
  }
  seq_int_att_dec_original_values[curr_att_dec][curr_att] = out_values;
}

Rans Decoding

DecodeSymbols()

void DecodeSymbols(num_symbols, num_components, out_values) {
  scheme                                                                              UI8
  if (scheme == TAGGED_SYMBOLS) {
    DecodeTaggedSymbols(num_symbols, num_components, out_values);
  } else if (scheme == RAW_SYMBOLS) {
    DecodeRawSymbols(num_symbols, out_values);
  }
}

DecodeTaggedSymbols

void DecodeTaggedSymbols(num_values, num_components, out_values) {
  num_symbols_                                                                        varUI32
  BuildSymbolTables(num_symbols_, lut_table_, probability_table_);
  size                                                                                varUI64
  encoded_data                                                                        UI8[size]
  RansInitDecoder(ans_decoder_, &encoded_data[0], size, TAGGED_RANS_BASE);
  for (i = 0; i < num_values; i += num_components) {
    RansRead(ans_decoder_, TAGGED_RANS_BASE, TAGGED_RANS_PRECISION,
             lut_table_, probability_table_, &size);
    for (j = 0; j < num_components; ++j) {
      val                                                                             f[size]
      out_values.push_back(val);
    }
    ResetBitReader();
  }
}

DecodeRawSymbols

void DecodeRawSymbols(num_values, out_values) {
  max_bit_length                                                                      UI8
  num_symbols_                                                                        varUI32
  rans_precision_bits  = (3 * max_bit_length) / 2;
  if (rans_precision_bits > 20)
    rans_precision_bits = 20;
  if (rans_precision_bits < 12)
    rans_precision_bits = 12;
  rans_precision = 1 << rans_precision_bits;
  l_rans_base = rans_precision * 4;
  BuildSymbolTables(num_symbols_, lut_table_, probability_table_);
  size                                                                                varUI64
  buffer                                                                              UI8[size]
  RansInitDecoder(ans_decoder_, &buffer[0], size, l_rans_base);
  for (i = 0; i < num_values; ++i) {
    RansRead(ans_decoder_, l_rans_base, rans_precision,
              lut_table_, probability_table_, &val);
    out_values.push_back(val);
  }
}

BuildSymbolTables

void BuildSymbolTables(num_symbols_, lut_table_, probability_table_) {
  for (i = 0; i < num_symbols_; ++i) {
    // Decode the first byte and extract the number of extra bytes we need to
    // get, or the offset to the next symbol with non-zero probability.
    prob_data                                                                         UI8
    token = prob_data & 3;
    if (token == 3) {
      offset = prob_data >> 2;
      for (j = 0; j < offset + 1; ++j) {
        token_probs[i + j] = 0;
      }
      i += offset;
    } else {
      prob = prob_data >> 2;
      for (j = 0; j < token; ++j) {
        eb                                                                            UI8
        prob = prob | (eb << (8 * (j + 1) - 2));
      }
      token_probs[i] = prob;
    }
  }
  rans_build_look_up_table(&token_probs[0], num_symbols_, lut_table_,
                           probability_table_);
}

rans_build_look_up_table

void rans_build_look_up_table(
    token_probs[], num_symbols, lut_table_, probability_table_) {
  cum_prob = 0;
  act_prob = 0;
  for (i = 0; i < num_symbols; ++i) {
    probability_table_[i].prob = token_probs[i];
    probability_table_[i].cum_prob = cum_prob;
    cum_prob += token_probs[i];
    for (j = act_prob; j < cum_prob; ++j) {
      lut_table_[j] = i;
    }
    act_prob = cum_prob;
  }
}

RansInitDecoder

void RansInitDecoder(ans, buf, offset, l_rans_base) {
  ans.buf = buf;
  x = buf[offset - 1] >> 6;
  if (x == 0) {
    ans.buf_offset = offset - 1;
    ans.state = buf[offset - 1] & 0x3F;
  } else if (x == 1) {
    ans.buf_offset = offset - 2;
    ans.state = mem_get_le16(buf + offset - 2) & 0x3FFF;
  } else if (x == 2) {
    ans.buf_offset = offset - 3;
    ans.state = mem_get_le24(buf + offset - 3) & 0x3FFFFF;
  } else if (x == 3) {
    ans.buf_offset = offset - 4;
    ans.state = mem_get_le32(buf + offset - 4) & 0x3FFFFFFF;
  }
  ans.state += l_rans_base;
}

RansRead

void RansRead(ans, l_rans_base, rans_precision,
              lut_table_, probability_table_, val) {
  while (ans.state < l_rans_base && ans.buf_offset > 0) {
    ans.state = ans.state * IO_BASE + ans.buf[--ans.buf_offset];
  }
  quo = ans.state / rans_precision;
  rem = ans.state % rans_precision;
  fetch_sym(&sym, rem, lut_table_, probability_table_);
  ans.state = quo * sym.prob + rem - sym.cum_prob;
  val = sym.val;
}

fetch_sym

void fetch_sym(sym, rem, lut_table_, probability_table_) {
  symbol = lut_table_[rem];
  sym.val = symbol;
  sym.prob = probability_table_[symbol].prob;
  sym.cum_prob = probability_table_[symbol].cum_prob;
}

RabsDescRead

void RabsDescRead(ans, p0, out_val) {
  p = rabs_ans_p8_precision - p0;
  if (ans.state < rabs_l_base) {
    ans.state = ans.state * IO_BASE + ans.buf[--ans.buf_offset];
  }
  x = ans.state;
  quot = x / rabs_ans_p8_precision;
  rem = x % rabs_ans_p8_precision;
  xn = quot * p;
  val = rem < p;
  if (val) {
    ans.state = xn + rem;
  } else {
    ans.state = x - xn - p;
  }
  out_val = val;
}

Corners

Next()

int Next(corner) {
  if (corner < 0)
    return corner;
  return ((corner % 3) == 2) ? corner - 2 : corner + 1;
}

Previous()

int Previous(corner) {
  if (corner < 0)
    return corner;
  return ((corner % 3) == 0) ? corner + 2 : corner - 1;
}

PosOpposite()

int PosOpposite(c) {
  if (c >= opposite_corners_.size())
    return -1;
  return opposite_corners_[c];
}

AttrOpposite()

int AttrOpposite(attr, corner) {
  if (IsCornerOppositeToSeamEdge(corner))
    return -1;
  return PosOpposite(corner);
}

Opposite()

int Opposite(att_dec, c) {
  if (att_dec == 0 || att_dec_decoder_type[att_dec] == MESH_VERTEX_ATTRIBUTE)
    return PosOpposite(c);
  return AttrOpposite(att_dec - 1, c);
}

GetLeftCorner()

int GetLeftCorner(corner_id) {
  if (corner_id < 0)
    return kInvalidCornerIndex;
  return PosOpposite(Previous(corner_id));
}

GetRightCorner()

int GetRightCorner(corner_id) {
  if (corner_id < 0)
    return kInvalidCornerIndex;
  return PosOpposite(Next(corner_id));
}

SwingRight()

int SwingRight(attr_dec, corner) {
  return Previous(Opposite(attr_dec, Previous(corner)));
}

SwingLeft()

int SwingLeft(attr_dec, corner) {
  return Next(Opposite(attr_dec, Next(corner)));
}

CornerToVert()

int CornerToVert(att_dec, corner_id) {
  CornerToVerts(att_dec, corner_id, &v, &n, &p);
  return v;
}

CornerToVertsInternal()

void CornerToVertsInternal(ftv, corner_id, v, n, p) {
  local = corner_id % 3;
  face = corner_id / 3;
  if (local == 0) {
    v = ftv[0][face];
    n = ftv[1][face];
    p = ftv[2][face];
  } else if (local == 1) {
    v = ftv[1][face];
    n = ftv[2][face];
    p = ftv[0][face];
  } else if (local == 2) {
    v = ftv[2][face];
    n = ftv[0][face];
    p = ftv[1][face];
  }
}

CornerToVerts()

void CornerToVerts(att_dec, corner_id, v, n, p) {
  if (att_dec == 0) {
    return CornerToVertsInternal(face_to_vertex, corner_id, v, n, p);
  } else {
    if (att_dec_decoder_type[att_dec] == MESH_VERTEX_ATTRIBUTE) {
      return CornerToVertsInternal(face_to_vertex, corner_id, v, n, p);
    } else {
      return CornerToVertsInternal(attr_face_to_vertex[att_dec - 1], corner_id,
                                   v, n, p);
    }
  }
}

SetOppositeCorners()

void SetOppositeCorners(c, opp_c) {
  opposite_corners_[c] = opp_c;
  opposite_corners_[opp_c] = c;
}

MapCornerToVertex()

void MapCornerToVertex(corner_id, vert_id) {
  corner_to_vertex_map_[0][corner_id] = vert_id;
  if (vert_id >= 0) {
    vertex_corners_[vert_id] = corner_id;
  }
}

UpdateVertexToCornerMap()

void UpdateVertexToCornerMap(vert) {
  first_c = vertex_corners_[vert];
  if (first_c < 0)
    return;
  act_c = SwingLeft(0, first_c);
  c = first_c;
  while (act_c >= 0 && act_c != first_c) {
    c = act_c;
    act_c = SwingLeft(0, act_c);
  }
  if (act_c != first_c) {
    vertex_corners_[vert] = c;
  }
}

Vectors

Dot()

void Dot(vec_x, vec_y, dot) {
  dot = 0;
  for (i = 0; i < vec_x.size(); ++i) {
    dot += vec_x[i] * vec_y[i];
  }
}

AbsSum()

void AbsSum(vec, abs_sum) {
  result = 0;
  for (i = 0; i < vec.size(); ++i) {
    result += Abs(vec[i]);
  }
  abs_sum = result;
}

MultiplyScalar()

void MultiplyScalar(vec, value, out) {
  for (i = 0; i < vec.size(); ++i) {
    out.push_back(vec[i] * value);
  }
}

DivideScalar()

void DivideScalar(vec, value, out) {
  for (i = 0; i < vec.size(); ++i) {
    out.push_back(vec[i] / value);
  }
}

AddVectors()

void AddVectors(a, b, c) {
  for (i = 0; i < a.size(); ++i) {
    c.push_back(a[i] + b[i]);
  }
}

SubtractVectors()

void SubtractVectors(a, b, c) {
  for (i = 0; i < a.size(); ++i) {
    c.push_back(a[i] - b[i]);
  }
}

CrossProduct()

void CrossProduct(u, v, r) {
  r[0] = (u[1] * v[2]) - (u[2] * v[1]);
  r[1] = (u[2] * v[0]) - (u[0] * v[2]);
  r[2] = (u[0] * v[1]) - (u[1] * v[0]);
}

Core Functions

LEB128

uint64_t LEB128() {
  result = 0;
  shift = 0;
  while(true) {
    in                                                                                UI8
    result |= (low order 7 bits of in) << shift;
    if (high order bit of in == 0)
      break;
    shift += 7;
  }
  return result;
}

mem_get_le16

uint32_t mem_get_le16(mem) {
  val = mem[1] << 8;
  val |= mem[0];
  return val;
}

mem_get_le24

uint32_t mem_get_le24(mem) {
  val = mem[2] << 16;
  val |= mem[1] << 8;
  val |= mem[0];
  return val;
}

mem_get_le32

uint32_t mem_get_le32(mem) {
  val = mem[3] << 24;
  val |= mem[2] << 16;
  val |= mem[1] << 8;
  val |= mem[0];
  return val;
}

Descriptions

Constants

  • Mesh encoding methods
    • 0: MESH_SEQUENTIAL_ENCODING
    • 1: MESH_EDGEBREAKER_ENCODING
  • Metadata constants
    • 32768: METADATA_FLAG_MASK
  • Sequential attribute encoding methods
    • 0: SEQUENTIAL_ATTRIBUTE_ENCODER_GENERIC
    • 1: SEQUENTIAL_ATTRIBUTE_ENCODER_INTEGER
    • 2: SEQUENTIAL_ATTRIBUTE_ENCODER_QUANTIZATION
    • 3: SEQUENTIAL_ATTRIBUTE_ENCODER_NORMALS
  • Sequential indices encoding methods
    • 0: SEQUENTIAL_COMPRESSED_INDICES
    • 1: SEQUENTIAL_UNCOMPRESSED_INDICES
  • Prediction encoding methods
    • -2: PREDICTION_NONE
    • 0: PREDICTION_DIFFERENCE
    • 1: MESH_PREDICTION_PARALLELOGRAM
    • 4: MESH_PREDICTION_CONSTRAINED_MULTI_PARALLELOGRAM
    • 5: MESH_PREDICTION_TEX_COORDS_PORTABLE
    • 6: MESH_PREDICTION_GEOMETRIC_NORMAL
  • Prediction scheme transform methods
    • 1: PREDICTION_TRANSFORM_WRAP
    • 3: PREDICTION_TRANSFORM_NORMAL_OCTAHEDRON_CANONICALIZED
  • Mesh traversal methods
    • 0: MESH_TRAVERSAL_DEPTH_FIRST
    • 1: MESH_TRAVERSAL_PREDICTION_DEGREE
  • Mesh attribute encoding methods
    • 0: MESH_VERTEX_ATTRIBUTE
    • 1: MESH_CORNER_ATTRIBUTE
  • EdgeBreaker encoding methods
    • 0: STANDARD_EDGEBREAKER
    • 2: VALENCE_EDGEBREAKER
  • EdgeBreaker constants
    • -1: kInvalidCornerIndex
    • 0: LEFT_FACE_EDGE
    • 1: RIGHT_FACE_EDGE
    • 2: kTexCoordsNumComponents
    • 4: kMaxNumParallelograms
    • 3: kMaxPriority
  • EdgeBreaker bit pattern constants
    • 0: TOPOLOGY_C
    • 1: TOPOLOGY_S
    • 3: TOPOLOGY_L
    • 5: TOPOLOGY_R
    • 7: TOPOLOGY_E
  • Valence EdgeBreaker constants
    • 2: MIN_VALENCE
    • 7: MAX_VALENCE
    • 6: NUM_UNIQUE_VALENCES
  • ANS constants
    • 256: rabs_ans_p8_precision
    • 1024: rabs_ans_p10_precision
    • 4096: rabs_l_base
    • 256: IO_BASE
    • 4096: L_RANS_BASE
    • 16384: TAGGED_RANS_BASE
    • 4096: TAGGED_RANS_PRECISION
  • Symbol encoding methods
    • 0: TAGGED_SYMBOLS
    • 1: RAW_SYMBOLS

Variables

  • draco_string
    • Must equal “DRACO”
  • major_version
    • bitstream major version number
  • minor_version
    • bitstream minor version number
  • encoder_type
    • 0: POINT_CLOUD
    • 1: TRIANGULAR_MESH
  • encoder_method
    • 0: MESH_SEQUENTIAL_ENCODING
    • 1: MESH_EDGEBREAKER_ENCODING
  • flags

Metadata

  • num_att_metadata
    • Attribute metadata count
  • att_metadata_id
    • Array of attribute metadata ids
  • att_metadata
    • Array of attribute metadata
  • file_metadata
    • Global metadata

Sequential Encoding

  • num_points
    • Number of encoded points
  • connectivity_method

EdgeBreaker Encoding

  • edgebreaker_traversal_type
    • 0: MeshEdgeBreakerTraversalDecoder
    • 1: MeshEdgeBreakerTraversalPredictiveDecoder
    • 2: MeshEdgeBreakerTraversalValenceDecoder
  • num_new_vertices
    • Number of new vertices
  • num_encoded_vertices
    • Number of encoded vertices
  • num_faces
    • Number of encoded faces
  • num_attribute_data
    • Number of encoded attributes
  • num_encoded_symbols
    • Number of encoded EdgeBreaker symbols
  • num_encoded_split_symbols
    • Number of encoded EdgeBreaker split symbols
  • encoded_connectivity_size
    • Size of encoded connectivity data in bytes
  • num_topology_splits
  • source_id_delta
    • Array of delta encoded source symbol ids
  • split_id_delta
    • Array of delta encoded split symbol ids
  • source_edge_bit
    • Array of source edge types
    • 0: LEFT_FACE_EDGE
    • 1: RIGHT_FACE_EDGE
  • source_symbol_id
    • Array of source symbol ids
  • split_symbol_id
    • Array of split symbol ids
  • last_symbol_
    • Last EdgeBreaker symbol decoded
  • last_vert_added
    • Id of the last vertex decoded
  • active_corner_stack
    • Array of current working corners used during EdgeBreaker decoding
  • edge_breaker_symbol_to_topology_id
    • Array of EdgeBreaker symbols
    • 0: TOPOLOGY_C
    • 1: TOPOLOGY_S
    • 2: TOPOLOGY_L
    • 3: TOPOLOGY_R
    • 4: TOPOLOGY_E
  • topology_split_id
    • List of decoder split ids encountered during a topology split.
  • split_active_corners
    • List of corners encountered during a topology split.

EdgeBreaker Traversal

  • eb_symbol_buffer_size
  • eb_symbol_buffer
    • Standard EdgeBreaker encoded symbol data
  • eb_start_face_buffer_prob_zero
    • Face configuration encoded probability
  • eb_start_face_buffer_size
  • eb_start_face_buffer
    • EdgeBreaker encoded face configuration data
  • attribute_connectivity_decoders_prob_zero
    • Array of encoded attribute probabilities
  • attribute_connectivity_decoders_size
    • Array of attribute connectivity size
  • attribute_connectivity_decoders_buffer
    • Array of attribute connectivity data

EdgeBreaker Valence Traversal

  • ebv_context_counters
    • Array of number of context symbols
  • ebv_context_symbols
    • Array of encoded context symbol data
  • active_context_
    • Index to the current valence
  • vertex_valences_
    • Array of current vertices valences

Attribute Encoding

  • num_attributes_decoders
  • att_dec_data_id
    • Array of attribute decoder ids
  • att_dec_decoder_type
    • Array of attribute decoder types
    • 0: MESH_VERTEX_ATTRIBUTE
    • 1: MESH_CORNER_ATTRIBUTE
  • att_dec_traversal_method
    • Array of attribute traversal methods
    • 0: MESH_TRAVERSAL_DEPTH_FIRST
    • 1: MESH_TRAVERSAL_PREDICTION_DEGREE
  • att_dec_num_values_to_decode
    • Number of values to decode per attribute
  • att_dec_num_attributes
    • Array of number of attributes encoded per attribute type
  • att_dec_att_type
  • att_dec_data_type
    • Attribute’s data type
  • att_dec_num_components
    • Attribute’s component count
  • att_dec_normalized
  • att_dec_unique_id
    • Attribute’s unique encoded id
  • seq_att_dec_decoder_type
    • Array of attribute encoding type
    • 0: SEQUENTIAL_ATTRIBUTE_ENCODER_GENERIC
    • 1: SEQUENTIAL_ATTRIBUTE_ENCODER_INTEGER
    • 2: SEQUENTIAL_ATTRIBUTE_ENCODER_QUANTIZATION
    • 3: SEQUENTIAL_ATTRIBUTE_ENCODER_NORMALS
  • seq_att_dec_prediction_scheme
    • Array of attribute prediction scheme method
    • -2: PREDICTION_NONE
    • 0: PREDICTION_DIFFERENCE
    • 1: MESH_PREDICTION_PARALLELOGRAM
    • 4: MESH_PREDICTION_CONSTRAINED_MULTI_PARALLELOGRAM
    • 5: MESH_PREDICTION_TEX_COORDS_PORTABLE
    • 6: MESH_PREDICTION_GEOMETRIC_NORMAL
  • seq_att_dec_prediction_transform_type
    • Array of attribute prediction transform method
    • 1: PREDICTION_TRANSFORM_WRAP
    • 3: PREDICTION_TRANSFORM_NORMAL_OCTAHEDRON_CANONICALIZED
  • seq_int_att_dec_compressed
  • pred_cons_multi_is_cease_edge
    • Array for multi parallelogram prediction signifying if the edge is the last edge
  • pred_tex_coords_orientations
    • Array signifying orientation for the texture coordinate prediction
  • pred_trasnform_wrap_min
    • Array of minimum clamp values used by the wrap transform
  • pred_trasnform_wrap_max
    • Array of maximum clamp values used by the wrap transform
  • pred_trasnform_normal_max_q_val
    • Maximum quantization array used by the normal transform
  • pred_transform_normal_flip_normal_bits
    • Array of flags used by the normal transform
  • seq_int_att_dec_decoded_values
    • Array of attribute decoded symbols
  • seq_int_att_dec_symbols_to_signed_ints
    • Array of decoded symbols converted to signed ints
  • seq_int_att_dec_original_values
    • Array containing the attribute’s original quantized values
  • seq_int_att_dec_dequantized_values
    • Array containing the attribute’s original values
  • quantized_data_min_values
    • Array of minimum quantization values
  • quantized_data_max_value_df
    • Array of quantization range
  • quantized_data_quantization_bits
    • Array of number of quantization bits

Attribute Traversal

  • curr_att_dec
    • Current attribute decoder type
  • curr_att
    • Current attribute within a decoder type
  • vertex_visited_point_ids
    • Array of the last vertex visited per attribute
  • att_connectivity_seam_opp
  • att_connectivity_seam_src
  • att_connectivity_seam_dest
  • corner_to_point_map
  • is_edge_on_seam_
    • Array of bools signifying if the corner’s opposite edge is on a seam
  • encoded_attribute_value_index_to_corner_map
    • Array for storing the corner ids in the order their associated attribute entries were encoded
  • vertex_to_encoded_attribute_value_index_map
    • Array for storing encoding order of attribute entries for each vertex
  • indices_map_
  • prediction_rans_prob_zero
    • Current rans zero probability
  • prediction_rans_data_size
    • Current size of rans encoded data
  • prediction_rans_data_buffer
    • Ans encoded prediction data for an attribute
  • tex_coords_num_orientations
    • Current number of orientations for encoded Texture data
  • traversal_stacks_
    • Array of available corners
  • best_priority_
    • Current best available priority
  • prediction_degree_
    • Array of current degree prediction for each vertex
  • constrained_multi_num_flags