geometry_octree_encoder.cpp 20.3 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
/* The copyright in this software is being made available under the BSD
 * Licence, included below.  This software may be subject to other third
 * party and contributor rights, including patent rights, and no such
 * rights are granted under this licence.
 *
 * Copyright (c) 2017-2018, ISO/IEC
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * * Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 *
 * * Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the distribution.
 *
 * * Neither the name of the ISO/IEC nor the names of its contributors
 *   may be used to endorse or promote products derived from this
 *   software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

36
#include "geometry.h"
37

38
#include "DualLutCoder.h"
39
#include "OctreeNeighMap.h"
40
#include "geometry_octree.h"
41
#include "geometry_intra_pred.h"
42
43
44
#include "io_hls.h"
#include "tables.h"

45
46
namespace pcc {

47
48
49
50
//============================================================================

class GeometryOctreeEncoder {
public:
51
  GeometryOctreeEncoder(
52
    const GeometryParameterSet& gps, EntropyEncoder* arithmeticEncoder);
53
54

  void beginOctreeLevel();
55
56
57

  int encodePositionLeafNumPoints(int count);

58
  void encodeOccupancyNeighZ(
59
60
61
62
63
    int mappedOccupancy,
    int mappedOccIsPredicted,
    int mappedOccPrediction,
    int mappedOccAdjGt0,
    int mappedOccAdjGt1);
64

65
66
67
68
  void encodeOccupancyNeighNZ(
    int neighPattern10,
    int mappedOccupancy,
    int mappedOccIsPredicted,
69
70
71
    int mappedOccPrediction,
    int mappedOccAdjGt0,
    int mappedOccAdjGt1);
72

73
74
75
76
  void encodeOccupancyBitwise(
    int neighPattern,
    int mappedOccupancy,
    int mappedOccIsPredicted,
77
78
79
    int mappedOccPrediction,
    int mappedOccAdjGt0,
    int mappedOccAdjGt1);
80

81
  void encodeOccupancyBytewise(int neighPattern, int mappedOccupancy);
82

83
84
85
86
  void encodeOccupancy(
    int neighPattern,
    int occupancy,
    int occupancyIsPredicted,
87
88
89
    int occupancyPrediction,
    int occupancyAdjGt0,
    int occupancyAdjGt1);
90

91
  void encodePointPosition(int nodeSizeLog2, const Vec3<uint32_t>& pos);
92
93
94
95
96
97
98

  bool encodeDirectPosition(
    int nodeSizeLog2,
    const PCCOctree3Node& node,
    const PCCPointSet3& pointCloud);

private:
99
100
101
  // selects between the bitwise and bytewise occupancy coders
  const bool _useBitwiseOccupancyCoder;

102
103
  const uint8_t (&_neighPattern64toR1)[64];

104
105
106
107
108
109
110
  EntropyEncoder* _arithmeticEncoder;
  StaticBitModel _ctxEquiProb;
  AdaptiveBitModel _ctxSingleChild;
  AdaptiveBitModel _ctxSinglePointPerBlock;
  AdaptiveBitModel _ctxPointCountPerBlock;
  AdaptiveBitModel _ctxBlockSkipTh;
  AdaptiveBitModel _ctxNumIdcmPointsEq1;
111
112

  // For bitwise occupancy coding
113
114
115
116
117
118
  //   maps 0 + i: no adjacency
  //   maps 3 + i: adjacency > 0
  //   maps 6 + i: adjacency > 1
  //   map i = 0 = not predicted
  //   map i = 1 = predicted unnoccupied
  //   map i = 2 = predicted occupied
119
  CtxModelOctreeOccupancy _ctxOccupancy;
120
  CtxMapOctreeOccupancy _ctxIdxMaps[9];
121
122
123

  // For bytewise occupancy coding
  DualLutCoder<true> _bytewiseOccupancyCoder[10];
124
125
126
127
128
};

//============================================================================

GeometryOctreeEncoder::GeometryOctreeEncoder(
129
  const GeometryParameterSet& gps, EntropyEncoder* arithmeticEncoder)
130
  : _useBitwiseOccupancyCoder(gps.bitwise_occupancy_coding_flag)
131
  , _neighPattern64toR1(neighPattern64toR1(gps))
132
  , _arithmeticEncoder(arithmeticEncoder)
133
  , _ctxOccupancy(gps.geom_occupancy_ctx_reduction_factor)
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
{
  if (!_useBitwiseOccupancyCoder) {
    for (int i = 0; i < 10; i++)
      _bytewiseOccupancyCoder[i].init(kDualLutOccupancyCoderInit[i]);
  }
}

//============================================================================

void
GeometryOctreeEncoder::beginOctreeLevel()
{
  for (int i = 0; i < 10; i++) {
    _bytewiseOccupancyCoder[i].resetLut();
  }
}
150

151
152
153
154
//============================================================================
// Encode the number of points in a leaf node of the octree.

int
155
GeometryOctreeEncoder::encodePositionLeafNumPoints(int count)
156
157
{
  if (count == 1) {
158
    _arithmeticEncoder->encode(1, _ctxSinglePointPerBlock);
159
  } else {
160
    _arithmeticEncoder->encode(0, _ctxSinglePointPerBlock);
161
    _arithmeticEncoder->encodeExpGolomb(
162
      uint32_t(count - 2), 0, _ctxEquiProb, _ctxPointCountPerBlock);
163
164
165
166
167
168
169
170
171
  }

  return count;
}

//-------------------------------------------------------------------------
// encode occupancy bits (neighPattern10 == 0 case)

void
172
GeometryOctreeEncoder::encodeOccupancyNeighZ(
173
174
175
176
177
  int mappedOccupancy,
  int mappedOccIsPredicted,
  int mappedOccPrediction,
  int mappedOccAdjGt0,
  int mappedOccAdjGt1)
178
{
179
  int minOccupied = 2;
180
181
  int numOccupiedAcc = 0;

182
  for (int i = 0; i < 8; i++) {
183
184
185
186
187
    int bit = (mappedOccupancy >> kOccBitCodingOrder[i]) & 1;
    int bitIsPredicted = (mappedOccIsPredicted >> kOccBitCodingOrder[i]) & 1;
    int bitPrediction = (mappedOccPrediction >> kOccBitCodingOrder[i]) & 1;
    int bitAdjGt0 = (mappedOccAdjGt0 >> kOccBitCodingOrder[i]) & 1;
    int bitAdjGt1 = (mappedOccAdjGt1 >> kOccBitCodingOrder[i]) & 1;
188

189
190
    int ctxIdxMapIdx =
      3 * (bitAdjGt0 + bitAdjGt1) + bitIsPredicted + bitPrediction;
191
    auto& ctxIdxMap = _ctxIdxMaps[ctxIdxMapIdx];
192
193

    int idx = numOccupiedAcc;
194
    int ctxIdx = ctxIdxMap.evolve(bit, &ctxIdxMap[i][idx]);
195

196
197
    // NB: There must be at least minOccupied child nodes
    //  -- avoid coding the occupancyBit if it is implied.
198
    if (numOccupiedAcc >= minOccupied + i - 7) {
199
      _arithmeticEncoder->encode(bit, _ctxOccupancy[ctxIdx]);
200
201
    }

202
    numOccupiedAcc += bit;
203
  }
204
205
206
207
208
209
}

//-------------------------------------------------------------------------
// encode occupancy bits (neighPattern10 != 0 case)

void
210
GeometryOctreeEncoder::encodeOccupancyNeighNZ(
211
212
213
  int neighPattern10,
  int mappedOccupancy,
  int mappedOccIsPredicted,
214
215
216
  int mappedOccPrediction,
  int mappedOccAdjGt0,
  int mappedOccAdjGt1)
217
218
219
{
  int neighPattern7 = kNeighPattern10to7[neighPattern10];
  int neighPattern5 = kNeighPattern7to5[neighPattern7];
220
221
222
223
224
225

  uint32_t partialOccupancy = 0;

  // NB: it is impossible for pattern to be 0 (handled in Z case).
  for (int i = 0; i < 8; i++) {
    int idx;
226
227
    if (i < 6) {
      idx = ((neighPattern10 - 1) << i) + partialOccupancy + i + 1;
228
    } else if (i == 6) {
229
      idx = ((neighPattern7 - 1) << i) + partialOccupancy + i + 1;
230
    } else if (i == 7) {
231
      idx = ((neighPattern5 - 1) << i) + partialOccupancy + i + 1;
232
233
234
235
236
    } else {
      // work around clang -Wsometimes-uninitialized fault
      break;
    }

237
238
239
240
241
    int bit = (mappedOccupancy >> kOccBitCodingOrder[i]) & 1;
    int bitIsPredicted = (mappedOccIsPredicted >> kOccBitCodingOrder[i]) & 1;
    int bitPrediction = (mappedOccPrediction >> kOccBitCodingOrder[i]) & 1;
    int bitAdjGt0 = (mappedOccAdjGt0 >> kOccBitCodingOrder[i]) & 1;
    int bitAdjGt1 = (mappedOccAdjGt1 >> kOccBitCodingOrder[i]) & 1;
242

243
244
    int ctxIdxMapIdx =
      3 * (bitAdjGt0 + bitAdjGt1) + bitIsPredicted + bitPrediction;
245
246
247
    auto& ctxIdxMap = _ctxIdxMaps[ctxIdxMapIdx];

    int ctxIdx = ctxIdxMap.evolve(bit, &ctxIdxMap[i][idx]);
248
249
250

    // NB: if firt 7 bits are 0, then the last is implicitly 1.
    if (i < 7 || partialOccupancy)
251
      _arithmeticEncoder->encode(bit, _ctxOccupancy[ctxIdx]);
252

253
    partialOccupancy |= bit << i;
254
  }
255
256
257
}

//-------------------------------------------------------------------------
258

259
void
260
GeometryOctreeEncoder::encodeOccupancyBitwise(
261
262
263
  int neighPattern,
  int mappedOccupancy,
  int mappedOccIsPredicted,
264
265
266
  int mappedOccPrediction,
  int mappedOccAdjGt0,
  int mappedOccAdjGt1)
267
{
268
  if (neighPattern == 0) {
269
    encodeOccupancyNeighZ(
270
271
      mappedOccupancy, mappedOccIsPredicted, mappedOccPrediction,
      mappedOccAdjGt0, mappedOccAdjGt1);
272
273
274
    return;
  }

275
  // code occupancy using the neighbour configuration context
276
277
  // with reduction from 64 states to 10 (or 6).
  int neighPatternR1 = _neighPattern64toR1[neighPattern];
278
  encodeOccupancyNeighNZ(
279
280
    neighPatternR1, mappedOccupancy, mappedOccIsPredicted, mappedOccPrediction,
    mappedOccAdjGt0, mappedOccAdjGt1);
281
}
282

283
284
285
286
//-------------------------------------------------------------------------

void
GeometryOctreeEncoder::encodeOccupancyBytewise(
287
  int neighPattern, int mappedOccupancy)
288
289
{
  // code occupancy using the neighbour configuration context
290
291
292
  // with reduction from 64 states to 10 (or 6).
  int neighPatternR1 = _neighPattern64toR1[neighPattern];
  auto& bytewiseCoder = _bytewiseOccupancyCoder[neighPatternR1];
293
294
295
  bytewiseCoder.encode(mappedOccupancy, _arithmeticEncoder);
}

296
297
298
//-------------------------------------------------------------------------
// decode node occupancy bits
//
299

300
void
301
GeometryOctreeEncoder::encodeOccupancy(
302
303
304
305
306
307
  int neighPattern,
  int occupancy,
  int occupancyIsPred,
  int occupancyPred,
  int occupancyAdjGt0,
  int occupancyAdjGt1)
308
309
{
  if (neighPattern == 0) {
310
    bool singleChild = !popcntGt1(occupancy);
311
    _arithmeticEncoder->encode(singleChild, _ctxSingleChild);
312
313
314

    if (singleChild) {
      // no siblings => encode index = (z,y,x) not 8bit pattern
315
316
317
      _arithmeticEncoder->encode(!!(occupancy & 0xaa), _ctxEquiProb);  // z
      _arithmeticEncoder->encode(!!(occupancy & 0xcc), _ctxEquiProb);  // y
      _arithmeticEncoder->encode(!!(occupancy & 0xf0), _ctxEquiProb);  // x
318
      return;
319
320
    }
  }
321

322
323
324
  uint32_t mapOcc = mapGeometryOccupancy(occupancy, neighPattern);
  uint32_t mapOccIsP = mapGeometryOccupancy(occupancyIsPred, neighPattern);
  uint32_t mapOccP = mapGeometryOccupancy(occupancyPred, neighPattern);
325
326
  uint32_t mapAdjGt0 = mapGeometryOccupancy(occupancyAdjGt0, neighPattern);
  uint32_t mapAdjGt1 = mapGeometryOccupancy(occupancyAdjGt1, neighPattern);
327
328

  if (_useBitwiseOccupancyCoder)
329
330
    encodeOccupancyBitwise(
      neighPattern, mapOcc, mapOccIsP, mapOccP, mapAdjGt0, mapAdjGt1);
331
  else
332
    encodeOccupancyBytewise(neighPattern, mapOcc);
333
334
335
336
337
338
}

//-------------------------------------------------------------------------
// Encode a position of a point in a given volume.

void
339
GeometryOctreeEncoder::encodePointPosition(
340
  int nodeSizeLog2, const Vec3<uint32_t>& pos)
341
342
{
  for (int mask = 1 << (nodeSizeLog2 - 1); mask; mask >>= 1) {
343
344
345
    _arithmeticEncoder->encode(!!(pos[0] & mask), _ctxEquiProb);
    _arithmeticEncoder->encode(!!(pos[1] & mask), _ctxEquiProb);
    _arithmeticEncoder->encode(!!(pos[2] & mask), _ctxEquiProb);
346
347
348
349
350
351
352
  }
}

//-------------------------------------------------------------------------
// Direct coding of position of points in node (early tree termination).

bool
353
354
GeometryOctreeEncoder::encodeDirectPosition(
  int nodeSizeLog2, const PCCOctree3Node& node, const PCCPointSet3& pointCloud)
355
356
357
{
  int numPoints = node.end - node.start;
  if (numPoints > MAX_NUM_DM_LEAF_POINTS) {
358
    _arithmeticEncoder->encode(0, _ctxBlockSkipTh);
359
360
361
    return false;
  }

362
363
  _arithmeticEncoder->encode(1, _ctxBlockSkipTh);
  _arithmeticEncoder->encode(numPoints > 1, _ctxNumIdcmPointsEq1);
364
365
366
367
368

  for (auto idx = node.start; idx < node.end; idx++) {
    // determine the point position relative to box edge
    encodePointPosition(
      nodeSizeLog2,
369
370
371
      Vec3<uint32_t>{int(pointCloud[idx][0]) - node.pos[0],
                     int(pointCloud[idx][1]) - node.pos[1],
                     int(pointCloud[idx][2]) - node.pos[2]});
372
373
374
375
376
377
378
379
  }

  return true;
}

//-------------------------------------------------------------------------

void
380
381
382
383
encodeGeometryOctree(
  const GeometryParameterSet& gps,
  const GeometryBrickHeader& gbh,
  PCCPointSet3& pointCloud,
384
  EntropyEncoder* arithmeticEncoder,
385
  pcc::ringbuf<PCCOctree3Node>* nodesRemaining)
386
{
387
  GeometryOctreeEncoder encoder(gps, arithmeticEncoder);
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413

  // init main fifo
  //  -- worst case size is the last level containing every input poit
  //     and each point being isolated in the previous level.
  pcc::ringbuf<PCCOctree3Node> fifo(pointCloud.getPointCount() + 1);

  // push the first node
  fifo.emplace_back();
  PCCOctree3Node& node00 = fifo.back();
  node00.start = uint32_t(0);
  node00.end = uint32_t(pointCloud.getPointCount());
  node00.pos = uint32_t(0);
  node00.neighPattern = 0;
  node00.numSiblingsPlus1 = 8;
  node00.siblingOccupancy = 0;

  // map of pointCloud idx to DM idx, used to reorder the points
  // after coding.
  std::vector<int> pointIdxToDmIdx(int(pointCloud.getPointCount()), -1);
  int nextDmIdx = 0;

  size_t processedPointCount = 0;
  std::vector<uint32_t> values;

  auto fifoCurrLvlEnd = fifo.end();

414
415
416
  // the initial node size is the root node's
  int nodeSizeLog2 = gbh.geom_max_node_size_log2;

417
418
419
420
421
  // this counter represents fifo.end() - fifoCurrLvlEnd().
  // ie, the number of nodes added to the next level of the tree
  int numNodesNextLvl = 0;

  MortonMap3D occupancyAtlas;
422
423
  if (gps.neighbour_avail_boundary_log2) {
    occupancyAtlas.resize(gps.neighbour_avail_boundary_log2);
424
425
    occupancyAtlas.clear();
  }
426
  Vec3<uint32_t> occupancyAtlasOrigin(0xffffffff);
427
428
429
430
431
432
433
434

  for (; !fifo.empty(); fifo.pop_front()) {
    if (fifo.begin() == fifoCurrLvlEnd) {
      // transition to the next level
      fifoCurrLvlEnd = fifo.end();
      numNodesNextLvl = 0;
      occupancyAtlasOrigin = 0xffffffff;
      nodeSizeLog2--;
435

436
437
      encoder.beginOctreeLevel();

438
      // allow partial tree encoding using trisoup
439
      if (nodeSizeLog2 == gps.trisoup_node_size_log2)
440
        break;
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
    }

    PCCOctree3Node& node0 = fifo.front();

    // split the current node into 8 children
    //  - perform an 8-way counting sort of the current node's points
    //  - (later) map to child nodes
    int childSizeLog2 = nodeSizeLog2 - 1;
    std::array<int, 8> childCounts = {};
    countingSort(
      PCCPointSet3::iterator(&pointCloud, node0.start),
      PCCPointSet3::iterator(&pointCloud, node0.end), childCounts,
      [=](const PCCPointSet3::Proxy& proxy) {
        const auto& point = *proxy;
        int bitpos = 1 << childSizeLog2;
        return !!(int(point[2]) & bitpos) | (!!(int(point[1]) & bitpos) << 1)
          | (!!(int(point[0]) & bitpos) << 2);
      });

    // generate the bitmap of child occupancy and count
    // the number of occupied children in node0.
    int occupancy = 0;
    int numSiblings = 0;
    for (int i = 0; i < 8; i++) {
      if (childCounts[i]) {
        occupancy |= 1 << i;
        numSiblings++;
      }
    }

471
472
473
    int occupancyAdjacencyGt0 = 0;
    int occupancyAdjacencyGt1 = 0;

474
    if (gps.neighbour_avail_boundary_log2) {
475
476
477
478
      updateGeometryOccupancyAtlas(
        node0.pos, nodeSizeLog2, fifo, fifoCurrLvlEnd, &occupancyAtlas,
        &occupancyAtlasOrigin);

479
480
481
482
      GeometryNeighPattern gnp = makeGeometryNeighPattern(
        gps.adjacent_child_contextualization_enabled_flag, node0.pos,
        nodeSizeLog2, occupancyAtlas);

483
484
485
      node0.neighPattern = gnp.neighPattern;
      occupancyAdjacencyGt0 = gnp.adjacencyGt0;
      occupancyAdjacencyGt1 = gnp.adjacencyGt1;
486
487
    }

488
489
490
    int occupancyIsPredicted = 0;
    int occupancyPrediction = 0;

491
492
493
494
495
496
497
    // generate intra prediction
    if (nodeSizeLog2 < gps.intra_pred_max_node_size_log2) {
      predictGeometryOccupancyIntra(
        occupancyAtlas, node0.pos, nodeSizeLog2, &occupancyIsPredicted,
        &occupancyPrediction);
    }

498
    // update atlas for advanced neighbours
499
500
501
502
    if (gps.neighbour_avail_boundary_log2) {
      updateGeometryOccupancyAtlasOccChild(
        node0.pos, nodeSizeLog2, occupancy, &occupancyAtlas);
    }
503

504
505
    // encode child occupancy map
    assert(occupancy > 0);
506
    encoder.encodeOccupancy(
507
508
      node0.neighPattern, occupancy, occupancyIsPredicted, occupancyPrediction,
      occupancyAdjacencyGt0, occupancyAdjacencyGt1);
509
510
511
512
513
514
515
516
517
518
519
520

    // when nodeSizeLog2 == 1, children are indivisible (ie leaf nodes)
    // and are immediately coded.  No further splitting occurs.
    if (nodeSizeLog2 == 1) {
      for (int i = 0; i < 8; i++) {
        if (!childCounts[i]) {
          // child is empty: skip
          continue;
        }

        // if the bitstream is configured to represent unique points,
        // no point count is sent.
521
        if (gps.geom_unique_points_flag) {
522
523
524
525
          assert(childCounts[i] == 1);
          continue;
        }

526
527
        encoder.encodePositionLeafNumPoints(childCounts[i]);
        processedPointCount += childCounts[i];
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
      }

      // leaf nodes do not get split
      continue;
    }

    // nodeSizeLog2 > 1: for each child:
    //  - determine elegibility for IDCM
    //  - directly code point positions if IDCM allowed and selected
    //  - otherwise, insert split children into fifo while updating neighbour state
    int childPointsStartIdx = node0.start;
    for (int i = 0; i < 8; i++) {
      if (!childCounts[i]) {
        // child is empty: skip
        continue;
      }

      // create new child
      fifo.emplace_back();
      auto& child = fifo.back();

      int x = !!(i & 4);
      int y = !!(i & 2);
      int z = !!(i & 1);

      child.pos[0] = node0.pos[0] + (x << childSizeLog2);
      child.pos[1] = node0.pos[1] + (y << childSizeLog2);
      child.pos[2] = node0.pos[2] + (z << childSizeLog2);

      child.start = childPointsStartIdx;
      childPointsStartIdx += childCounts[i];
      child.end = childPointsStartIdx;
      child.numSiblingsPlus1 = numSiblings;
      child.siblingOccupancy = occupancy;

563
      bool idcmEnabled = gps.inferred_direct_coding_mode_enabled_flag;
564
      if (isDirectModeEligible(idcmEnabled, nodeSizeLog2, node0, child)) {
565
566
        bool directModeUsed =
          encoder.encodeDirectPosition(childSizeLog2, child, pointCloud);
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586

        if (directModeUsed) {
          // point reordering to match decoder's order
          for (auto idx = child.start; idx < child.end; idx++)
            pointIdxToDmIdx[idx] = nextDmIdx++;

          // NB: by definition, this is the only child node present
          assert(child.numSiblingsPlus1 == 1);

          // remove leaf node from fifo: it has been consumed and will
          // not be further split.
          fifo.pop_back();
          break;
        }
      }

      numNodesNextLvl++;

      // NB: when neighbourAvailBoundaryLog2 is set, an alternative
      //     implementation is used to calculate neighPattern.
587
      if (!gps.neighbour_avail_boundary_log2) {
588
        updateGeometryNeighState(
589
590
          gps.neighbour_context_restriction_flag, fifo.end(), numNodesNextLvl,
          childSizeLog2, child, i, node0.neighPattern, occupancy);
591
592
593
594
      }
    }
  }

595
596
597
598
599
600
  // return partial coding result
  if (nodesRemaining) {
    *nodesRemaining = std::move(fifo);
    return;
  }

601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
  ////
  // The following is to re-order the points according in the decoding
  // order since IDCM causes leaves to be coded earlier than they
  // otherwise would.
  PCCPointSet3 pointCloud2;
  pointCloud2.addRemoveAttributes(
    pointCloud.hasColors(), pointCloud.hasReflectances());
  pointCloud2.resize(pointCloud.getPointCount());

  // copy points with DM points first, the rest second
  int outIdx = nextDmIdx;
  for (int i = 0; i < pointIdxToDmIdx.size(); i++) {
    int dstIdx = pointIdxToDmIdx[i];
    if (dstIdx == -1) {
      dstIdx = outIdx++;
    }

    pointCloud2[dstIdx] = pointCloud[i];
    if (pointCloud.hasColors())
      pointCloud2.setColor(dstIdx, pointCloud.getColor(i));
    if (pointCloud.hasReflectances())
      pointCloud2.setReflectance(dstIdx, pointCloud.getReflectance(i));
  }

  swap(pointCloud, pointCloud2);
}

//============================================================================

630
631
632
633
634
void
encodeGeometryOctree(
  const GeometryParameterSet& gps,
  const GeometryBrickHeader& gbh,
  PCCPointSet3& pointCloud,
635
  EntropyEncoder* arithmeticEncoder)
636
637
638
639
640
641
{
  encodeGeometryOctree(gps, gbh, pointCloud, arithmeticEncoder, nullptr);
}

//============================================================================

642
}  // namespace pcc