diff --git a/cfg/octree-liftt-ctc-lossless-geom-lossy-attrs.yaml b/cfg/octree-liftt-ctc-lossless-geom-lossy-attrs.yaml
index 32d3bde..464199f 100644
--- a/cfg/octree-liftt-ctc-lossless-geom-lossy-attrs.yaml
+++ b/cfg/octree-liftt-ctc-lossless-geom-lossy-attrs.yaml
@@ -70,7 +70,6 @@ categories:
      # attribute coding -- reflectance
      -
        - !conditional '${has_refl}'
-       - adaptivePredictionThreshold: '$eval{ 1 << (8 - 2) }'
        - qp:
            r01: 51
            r02: 46
@@ -85,7 +84,6 @@ categories:
      # attribute coding -- colour
      -
        - !conditional '${has_colour}'
-       - adaptivePredictionThreshold: '$eval{ 1 << (8 - 2) }'
        - qp:
            r01: 51
            r02: 46
diff --git a/cfg/octree-liftt-ctc-lossy-geom-lossy-attrs.yaml b/cfg/octree-liftt-ctc-lossy-geom-lossy-attrs.yaml
index d6108d4..dff4254 100644
--- a/cfg/octree-liftt-ctc-lossy-geom-lossy-attrs.yaml
+++ b/cfg/octree-liftt-ctc-lossy-geom-lossy-attrs.yaml
@@ -67,7 +67,6 @@ categories:
        # attribute coding -- reflectance
        -
          - !conditional '${has_refl}'
-         - adaptivePredictionThreshold: '$eval{ 1 << (8 - 2) }'
          - qp:
              r01: 51
              r02: 46
@@ -82,7 +81,6 @@ categories:
        # attribute coding -- colour
        -
          - !conditional '${has_colour}'
-         - adaptivePredictionThreshold: '$eval{ 1 << (8 - 2) }'
          - qp:
              r01: 51
              r02: 46
diff --git a/cfg/octree-predt-ctc-lossless-geom-lossless-attrs.yaml b/cfg/octree-predt-ctc-lossless-geom-lossless-attrs.yaml
index 7471a2f..025403d 100644
--- a/cfg/octree-predt-ctc-lossless-geom-lossless-attrs.yaml
+++ b/cfg/octree-predt-ctc-lossless-geom-lossless-attrs.yaml
@@ -75,7 +75,7 @@ categories:
        # attribute coding -- reflectance
        -
          - !conditional '${has_refl}'
-         - adaptivePredictionThreshold: '$eval{ 1 << (8 - 2) }'
+         - costOffsetForPredMode0: 30
          - qp: 4
          - bitdepth: 8
          - attribute: reflectance
@@ -84,7 +84,7 @@ categories:
        # attribute coding -- colour
        -
          - !conditional '${has_colour}'
-         - adaptivePredictionThreshold: '$eval{ 1 << (8 - 2) }'
+         - costOffsetForPredMode0: 30
          - qp: 4
          - qpChromaOffset: 0
          - bitdepth: 8
diff --git a/cfg/octree-predt-ctc-lossless-geom-nearlossless-attrs.yaml b/cfg/octree-predt-ctc-lossless-geom-nearlossless-attrs.yaml
index 16badff..fdc70f6 100644
--- a/cfg/octree-predt-ctc-lossless-geom-nearlossless-attrs.yaml
+++ b/cfg/octree-predt-ctc-lossless-geom-nearlossless-attrs.yaml
@@ -73,7 +73,7 @@ categories:
      # attribute coding -- reflectance
      -
        - !conditional '${has_refl}'
-       - adaptivePredictionThreshold: '$eval{ 1 << (8 - 2) }'
+       - costOffsetForPredMode0: 10
        - qp:
            r01: 10
            r02: 16
@@ -87,7 +87,7 @@ categories:
      # attribute coding -- colour
      -
        - !conditional '${has_colour}'
-       - adaptivePredictionThreshold: '$eval{ 1 << (8 - 2) }'
+       - costOffsetForPredMode0: 10
        - qp:
            r01: 10
            r02: 16
diff --git a/cfg/trisoup-liftt-ctc-lossy-geom-lossy-attrs.yaml b/cfg/trisoup-liftt-ctc-lossy-geom-lossy-attrs.yaml
index 050a242..f45d02b 100644
--- a/cfg/trisoup-liftt-ctc-lossy-geom-lossy-attrs.yaml
+++ b/cfg/trisoup-liftt-ctc-lossy-geom-lossy-attrs.yaml
@@ -39,7 +39,6 @@ categories:
      # attribute coding -- colour
      -
        - !conditional '${has_colour}'
-       - adaptivePredictionThreshold: '$eval{ 1 << (8 - 2) }'
        - qp:
            r01: 46
            r02: 40
diff --git a/tmc3/Attribute.h b/tmc3/Attribute.h
index b4548bb..b61a0ef 100644
--- a/tmc3/Attribute.h
+++ b/tmc3/Attribute.h
@@ -78,6 +78,20 @@ std::unique_ptr<AttributeDecoderIntf> makeAttributeDecoder();
 
 //============================================================================
 
+struct EncoderAttributeParams {
+  // NB: this only makes sense for setting configurable parameters
+  AttributeBrickHeader abh;
+
+  // Threshold for choosing dist2 out of the population of nearest neighbour
+  // distances.
+  float dist2PercentileEstimate;
+
+  // Cost offset for predmode selection on pred-lift
+  int costOffsetForPredMode0;
+};
+
+//============================================================================
+
 class AttributeEncoderIntf {
 public:
   virtual ~AttributeEncoderIntf();
@@ -89,7 +103,8 @@ public:
     AttributeBrickHeader& abh,
     AttributeContexts& ctxtMem,
     PCCPointSet3& pointCloud,
-    PayloadBuffer* payload) = 0;
+    PayloadBuffer* payload,
+    const EncoderAttributeParams& attr_enc) = 0;
 
   // Indicates if the attribute decoder can decode the given aps
   virtual bool isReusable(
diff --git a/tmc3/AttributeDecoder.cpp b/tmc3/AttributeDecoder.cpp
index e8e09c0..857871a 100644
--- a/tmc3/AttributeDecoder.cpp
+++ b/tmc3/AttributeDecoder.cpp
@@ -314,25 +314,7 @@ AttributeDecoder::computeReflectancePredictionWeights(
   PCCResidualsDecoder& decoder)
 {
   predictor.predMode = 0;
-  int64_t maxDiff = 0;
-
-  if (predictor.neighborCount > 1 && aps.max_num_direct_predictors) {
-    int64_t minValue = 0;
-    int64_t maxValue = 0;
-    for (int i = 0; i < predictor.neighborCount; ++i) {
-      const attr_t reflectanceNeighbor = pointCloud.getReflectance(
-        indexes[predictor.neighbors[i].predictorIndex]);
-      if (i == 0 || reflectanceNeighbor < minValue) {
-        minValue = reflectanceNeighbor;
-      }
-      if (i == 0 || reflectanceNeighbor > maxValue) {
-        maxValue = reflectanceNeighbor;
-      }
-    }
-    maxDiff = maxValue - minValue;
-  }
-
-  if (maxDiff >= aps.adaptive_prediction_threshold) {
+  if (aps.max_num_direct_predictors) {
     predictor.predMode = decoder.decodePredMode(aps.max_num_direct_predictors);
   }
 }
@@ -390,29 +372,8 @@ AttributeDecoder::computeColorPredictionWeights(
   PCCPredictor& predictor,
   PCCResidualsDecoder& decoder)
 {
-  int64_t maxDiff = 0;
-
-  if (predictor.neighborCount > 1 && aps.max_num_direct_predictors) {
-    int64_t minValue[3] = {0, 0, 0};
-    int64_t maxValue[3] = {0, 0, 0};
-    for (int i = 0; i < predictor.neighborCount; ++i) {
-      const Vec3<attr_t> colorNeighbor =
-        pointCloud.getColor(indexes[predictor.neighbors[i].predictorIndex]);
-      for (size_t k = 0; k < 3; ++k) {
-        if (i == 0 || colorNeighbor[k] < minValue[k]) {
-          minValue[k] = colorNeighbor[k];
-        }
-        if (i == 0 || colorNeighbor[k] > maxValue[k]) {
-          maxValue[k] = colorNeighbor[k];
-        }
-      }
-    }
-    maxDiff = (std::max)(
-      maxValue[2] - minValue[2],
-      (std::max)(maxValue[0] - minValue[0], maxValue[1] - minValue[1]));
-  }
-
-  if (maxDiff >= aps.adaptive_prediction_threshold) {
+  predictor.predMode = 0;
+  if (aps.max_num_direct_predictors) {
     predictor.predMode = decoder.decodePredMode(aps.max_num_direct_predictors);
   }
 }
diff --git a/tmc3/AttributeEncoder.cpp b/tmc3/AttributeEncoder.cpp
index 21fa42b..dbe9463 100644
--- a/tmc3/AttributeEncoder.cpp
+++ b/tmc3/AttributeEncoder.cpp
@@ -387,7 +387,8 @@ AttributeEncoder::encode(
   AttributeBrickHeader& abh,
   AttributeContexts& ctxtMem,
   PCCPointSet3& pointCloud,
-  PayloadBuffer* payload)
+  PayloadBuffer* payload,
+  const EncoderAttributeParams& attr_enc)
 {
   QpSet qpSet = deriveQpSet(desc, attr_aps, abh);
 
@@ -410,7 +411,7 @@ AttributeEncoder::encode(
       break;
 
     case AttributeEncoding::kPredictingTransform:
-      encodeReflectancesPred(desc, attr_aps, qpSet, pointCloud, encoder);
+      encodeReflectancesPred(desc, attr_aps, qpSet, pointCloud, encoder, attr_enc);
       break;
 
     case AttributeEncoding::kLiftingTransform:
@@ -424,7 +425,7 @@ AttributeEncoder::encode(
       break;
 
     case AttributeEncoding::kPredictingTransform:
-      encodeColorsPred(desc, attr_aps, qpSet, pointCloud, encoder);
+      encodeColorsPred(desc, attr_aps, qpSet, pointCloud, encoder, attr_enc);
       break;
 
     case AttributeEncoding::kLiftingTransform:
@@ -482,54 +483,40 @@ AttributeEncoder::computeReflectancePredictionWeights(
   PCCPredictor& predictor,
   PCCResidualsEncoder& encoder,
   PCCResidualsEntropyEstimator& context,
-  const Quantizer& quant)
+  const Quantizer& quant,
+  const EncoderAttributeParams& attr_enc)
 {
   predictor.predMode = 0;
-  predictor.maxDiff = 0;
-  if (predictor.neighborCount > 1 && aps.max_num_direct_predictors) {
-    int64_t minValue = 0;
-    int64_t maxValue = 0;
-    for (size_t i = 0; i < predictor.neighborCount; ++i) {
-      const uint64_t reflectanceNeighbor = pointCloud.getReflectance(
-        indexesLOD[predictor.neighbors[i].predictorIndex]);
-      if (i == 0 || reflectanceNeighbor < minValue) {
-        minValue = reflectanceNeighbor;
-      }
-      if (i == 0 || reflectanceNeighbor > maxValue) {
-        maxValue = reflectanceNeighbor;
-      }
-    }
-    const int64_t maxDiff = maxValue - minValue;
-    predictor.maxDiff = maxDiff;
-    if (maxDiff >= aps.adaptive_prediction_threshold) {
-      uint64_t attrValue =
-        pointCloud.getReflectance(indexesLOD[predictorIndex]);
-
-      // base case: start with the first neighbour
-      // NB: skip evaluation of mode 0 (weighted average of n neighbours)
-      predictor.predMode = 1;
-      uint64_t attrPred = predictor.predictReflectance(pointCloud, indexesLOD);
-      int64_t attrResidualQuant =
-        computeReflectanceResidual(attrValue, attrPred, quant);
+  if (aps.max_num_direct_predictors) {
+    uint64_t attrValue =
+      pointCloud.getReflectance(indexesLOD[predictorIndex]);
 
-      // NB: idxBits is not included in the score
-      int64_t best_score = attrResidualQuant;
+    uint64_t attrPred = predictor.predictReflectance(pointCloud, indexesLOD);
+    int64_t attrResidualQuant =
+      computeReflectanceResidual(attrValue, attrPred, quant);
 
-      for (int i = 1; i < predictor.neighborCount; i++) {
-        if (i == aps.max_num_direct_predictors)
-          break;
+    double best_score = attrResidualQuant + kAttrPredLambdaR
+      * (double)(quant.stepSize() >> kFixedPointAttributeShift);
+    best_score -= attr_enc.costOffsetForPredMode0;
 
-        attrPred = pointCloud.getReflectance(
-          indexesLOD[predictor.neighbors[i].predictorIndex]);
-        attrResidualQuant =
-          computeReflectanceResidual(attrValue, attrPred, quant);
+    for (int i = 0; i < predictor.neighborCount; i++) {
+      if (i == aps.max_num_direct_predictors)
+        break;
 
-        if (attrResidualQuant < best_score) {
-          best_score = attrResidualQuant;
-          predictor.predMode = i + 1;
-          // NB: setting predictor.neighborCount = 1 will cause issues
-          // with reconstruction.
-        }
+      attrPred = pointCloud.getReflectance(
+        indexesLOD[predictor.neighbors[i].predictorIndex]);
+      attrResidualQuant =
+        computeReflectanceResidual(attrValue, attrPred, quant);
+
+      double idxBits = i + (i == aps.max_num_direct_predictors - 1 ? 1 : 2);
+      double score = attrResidualQuant + idxBits * kAttrPredLambdaR
+        *(quant.stepSize() >> kFixedPointAttributeShift);
+
+      if (score < best_score) {
+        best_score = score;
+        predictor.predMode = i + 1;
+        // NB: setting predictor.neighborCount = 1 will cause issues
+        // with reconstruction.
       }
     }
   }
@@ -543,7 +530,8 @@ AttributeEncoder::encodeReflectancesPred(
   const AttributeParameterSet& aps,
   const QpSet& qpSet,
   PCCPointSet3& pointCloud,
-  PCCResidualsEncoder& encoder)
+  PCCResidualsEncoder& encoder,
+  const EncoderAttributeParams& attr_enc)
 {
   const uint32_t pointCount = pointCloud.getPointCount();
   const int64_t clipMax = (1ll << desc.bitdepth) - 1;
@@ -566,7 +554,7 @@ AttributeEncoder::encodeReflectancesPred(
 
     computeReflectancePredictionWeights(
       aps, pointCloud, _lods.indexes, predictorIndex, predictor, encoder,
-      context, quant[0]);
+      context, quant[0], attr_enc);
 
     const uint64_t reflectance = pointCloud.getReflectance(pointIndex);
     const attr_t predictedReflectance =
@@ -601,10 +589,8 @@ AttributeEncoder::encodeReflectancesPred(
   for (size_t predictorIndex = 0; predictorIndex < pointCount;
        ++predictorIndex) {
     auto& predictor = _lods.predictors[predictorIndex];
-    if (predictor.maxDiff >= aps.adaptive_prediction_threshold) {
-      encoder.encodePredMode(
-        predictor.predMode, aps.max_num_direct_predictors);
-    }
+    encoder.encodePredMode(
+      +predictor.predMode, aps.max_num_direct_predictors);
     if (zero_cnt > 0)
       zero_cnt--;
     else {
@@ -660,64 +646,44 @@ AttributeEncoder::computeColorPredictionWeights(
   PCCPredictor& predictor,
   PCCResidualsEncoder& encoder,
   PCCResidualsEntropyEstimator& context,
-  const Quantizers& quant)
+  const Quantizers& quant,
+  const EncoderAttributeParams& attr_enc)
 {
-  predictor.maxDiff = 0;
-  if (predictor.neighborCount > 1 && aps.max_num_direct_predictors) {
-    int64_t minValue[3] = {0, 0, 0};
-    int64_t maxValue[3] = {0, 0, 0};
-    for (int i = 0; i < predictor.neighborCount; ++i) {
-      const Vec3<attr_t> colorNeighbor =
-        pointCloud.getColor(indexesLOD[predictor.neighbors[i].predictorIndex]);
-      for (size_t k = 0; k < 3; ++k) {
-        if (i == 0 || colorNeighbor[k] < minValue[k]) {
-          minValue[k] = colorNeighbor[k];
-        }
-        if (i == 0 || colorNeighbor[k] > maxValue[k]) {
-          maxValue[k] = colorNeighbor[k];
-        }
-      }
-    }
-    const int64_t maxDiff = (std::max)(
-      maxValue[2] - minValue[2],
-      (std::max)(maxValue[0] - minValue[0], maxValue[1] - minValue[1]));
-    predictor.maxDiff = maxDiff;
-
-    if (maxDiff >= aps.adaptive_prediction_threshold) {
-      Vec3<attr_t> attrValue = pointCloud.getColor(indexesLOD[predictorIndex]);
-
-      // base case: weighted average of n neighbours
-      predictor.predMode = 0;
-      Vec3<attr_t> attrPred = predictor.predictColor(pointCloud, indexesLOD);
-      Vec3<int64_t> attrResidualQuant =
+  predictor.predMode = 0;
+  if (aps.max_num_direct_predictors) {
+    Vec3<attr_t> attrValue = pointCloud.getColor(indexesLOD[predictorIndex]);
+
+    // base case: weighted average of n neighbours
+    Vec3<attr_t> attrPred = predictor.predictColor(pointCloud, indexesLOD);
+    Vec3<int64_t> attrResidualQuant =
+      computeColorResiduals(aps, attrValue, attrPred, quant);
+
+    double best_score = attrResidualQuant[0] + attrResidualQuant[1]
+      + attrResidualQuant[2]
+      + kAttrPredLambdaC
+      * (double)(quant[0].stepSize() >> kFixedPointAttributeShift);
+    best_score -= attr_enc.costOffsetForPredMode0;
+
+    for (int i = 0; i < predictor.neighborCount; i++) {
+      if (i == aps.max_num_direct_predictors)
+        break;
+
+      attrPred = pointCloud.getColor(
+        indexesLOD[predictor.neighbors[i].predictorIndex]);
+      attrResidualQuant =
         computeColorResiduals(aps, attrValue, attrPred, quant);
 
-      double best_score = attrResidualQuant[0] + attrResidualQuant[1]
+      double idxBits = i + (i == aps.max_num_direct_predictors - 1 ? 1 : 2);
+      double score = attrResidualQuant[0] + attrResidualQuant[1]
         + attrResidualQuant[2]
-        + kAttrPredLambdaC
-          * (double)(quant[0].stepSize() >> kFixedPointAttributeShift);
-
-      for (int i = 0; i < predictor.neighborCount; i++) {
-        if (i == aps.max_num_direct_predictors)
-          break;
-
-        attrPred = pointCloud.getColor(
-          indexesLOD[predictor.neighbors[i].predictorIndex]);
-        attrResidualQuant =
-          computeColorResiduals(aps, attrValue, attrPred, quant);
-
-        double idxBits = i + (i == aps.max_num_direct_predictors - 1 ? 1 : 2);
-        double score = attrResidualQuant[0] + attrResidualQuant[1]
-          + attrResidualQuant[2]
-          + idxBits * kAttrPredLambdaC
-            * (quant[0].stepSize() >> kFixedPointAttributeShift);
-
-        if (score < best_score) {
-          best_score = score;
-          predictor.predMode = i + 1;
-          // NB: setting predictor.neighborCount = 1 will cause issues
-          // with reconstruction.
-        }
+        + idxBits * kAttrPredLambdaC
+        * (quant[0].stepSize() >> kFixedPointAttributeShift);
+
+      if (score < best_score) {
+        best_score = score;
+        predictor.predMode = i + 1;
+        // NB: setting predictor.neighborCount = 1 will cause issues
+        // with reconstruction.
       }
     }
   }
@@ -731,7 +697,8 @@ AttributeEncoder::encodeColorsPred(
   const AttributeParameterSet& aps,
   const QpSet& qpSet,
   PCCPointSet3& pointCloud,
-  PCCResidualsEncoder& encoder)
+  PCCResidualsEncoder& encoder,
+  const EncoderAttributeParams& attr_enc)
 {
   const size_t pointCount = pointCloud.getPointCount();
 
@@ -759,7 +726,7 @@ AttributeEncoder::encodeColorsPred(
 
     computeColorPredictionWeights(
       aps, pointCloud, _lods.indexes, predictorIndex, predictor, encoder,
-      context, quant);
+      context, quant, attr_enc);
     const Vec3<attr_t> color = pointCloud.getColor(pointIndex);
     const Vec3<attr_t> predictedColor =
       predictor.predictColor(pointCloud, _lods.indexes);
@@ -810,10 +777,8 @@ AttributeEncoder::encodeColorsPred(
   for (size_t predictorIndex = 0; predictorIndex < pointCount;
        ++predictorIndex) {
     auto& predictor = _lods.predictors[predictorIndex];
-    if (predictor.maxDiff >= aps.adaptive_prediction_threshold) {
-      encoder.encodePredMode(
-        predictor.predMode, aps.max_num_direct_predictors);
-    }
+    encoder.encodePredMode(
+      predictor.predMode, aps.max_num_direct_predictors);
     if (zero_cnt > 0)
       zero_cnt--;
     else {
diff --git a/tmc3/AttributeEncoder.h b/tmc3/AttributeEncoder.h
index f9229cb..073c74f 100644
--- a/tmc3/AttributeEncoder.h
+++ b/tmc3/AttributeEncoder.h
@@ -63,7 +63,8 @@ public:
     AttributeBrickHeader& abh,
     AttributeContexts& ctxtMem,
     PCCPointSet3& pointCloud,
-    PayloadBuffer* payload) override;
+    PayloadBuffer* payload,
+    const EncoderAttributeParams& attr_enc) override;
 
   bool isReusable(
     const AttributeParameterSet& aps,
@@ -91,14 +92,16 @@ protected:
     const AttributeParameterSet& aps,
     const QpSet& qpSet,
     PCCPointSet3& pointCloud,
-    PCCResidualsEncoder& encoder);
+    PCCResidualsEncoder& encoder,
+    const EncoderAttributeParams& attr_enc);
 
   void encodeColorsPred(
     const AttributeDescription& desc,
     const AttributeParameterSet& aps,
     const QpSet& qpSet,
     PCCPointSet3& pointCloud,
-    PCCResidualsEncoder& encoder);
+    PCCResidualsEncoder& encoder,
+    const EncoderAttributeParams& attr_enc);
 
   void encodeReflectancesTransformRaht(
     const AttributeDescription& desc,
@@ -128,7 +131,8 @@ protected:
     PCCPredictor& predictor,
     PCCResidualsEncoder& encoder,
     PCCResidualsEntropyEstimator& context,
-    const Quantizers& quant);
+    const Quantizers& quant,
+    const EncoderAttributeParams& attr_enc);
 
   static int64_t computeReflectanceResidual(
     const uint64_t reflectance,
@@ -143,7 +147,8 @@ protected:
     PCCPredictor& predictor,
     PCCResidualsEncoder& encoder,
     PCCResidualsEntropyEstimator& context,
-    const Quantizer& quant);
+    const Quantizer& quant,
+    const EncoderAttributeParams& attr_enc);
 
 private:
   std::vector<int8_t> computeLastComponentPredictionCoeff(
diff --git a/tmc3/PCCTMC3Common.h b/tmc3/PCCTMC3Common.h
index 5c6ab38..5d0bcdf 100644
--- a/tmc3/PCCTMC3Common.h
+++ b/tmc3/PCCTMC3Common.h
@@ -237,7 +237,6 @@ struct PCCPredictor {
   uint32_t neighborCount;
   PCCNeighborInfo neighbors[kAttributePredictionMaxNeighbourCount];
   int8_t predMode;
-  int64_t maxDiff;
 
   Vec3<attr_t> predictColor(
     const PCCPointSet3& pointCloud, const std::vector<uint32_t>& indexes) const
diff --git a/tmc3/PCCTMC3Encoder.h b/tmc3/PCCTMC3Encoder.h
index a37ae69..4db113c 100644
--- a/tmc3/PCCTMC3Encoder.h
+++ b/tmc3/PCCTMC3Encoder.h
@@ -54,17 +54,6 @@ namespace pcc {
 
 //============================================================================
 
-struct EncoderAttributeParams {
-  // NB: this only makes sense for setting configurable parameters
-  AttributeBrickHeader abh;
-
-  // Threshold for choosing dist2 out of the population of nearest neighbour
-  // distances.
-  float dist2PercentileEstimate;
-};
-
-//----------------------------------------------------------------------------
-
 struct EncoderParams {
   SequenceParameterSet sps;
   GeometryParameterSet gps;
diff --git a/tmc3/TMC3.cpp b/tmc3/TMC3.cpp
index 9756155..1916e59 100644
--- a/tmc3/TMC3.cpp
+++ b/tmc3/TMC3.cpp
@@ -851,12 +851,6 @@ ParseParameters(int argc, char* argv[], Parameters& params)
     params_attr.aps.num_pred_nearest_neighbours_minus1, 3,
     "Attribute's maximum number of nearest neighbors to be used for prediction")
 
-  ("adaptivePredictionThreshold",
-    params_attr.aps.adaptive_prediction_threshold, -1,
-    "Neighbouring attribute value difference that enables choice of "
-    "single|multi predictors. Applies to transformType=2 only.\n"
-    "  -1: auto = 2**(bitdepth-2)")
-
   ("intraLodSearchRange",
     params_attr.aps.intra_lod_search_range, 0,
     "Intra LoD nearest neighbor search range")
@@ -894,6 +888,10 @@ ParseParameters(int argc, char* argv[], Parameters& params)
     params_attr.encoder.dist2PercentileEstimate, 0.85f,
     "Percentile for dist2 estimation during nearest neighbour search")
 
+    ("costOffsetForPredMode0",
+      params_attr.encoder.costOffsetForPredMode0, 0,
+      "Cost offset for predmode0 on pred-lift")
+
   ("lodSamplingPeriod",
     params_attr.aps.lodSamplingPeriod, {4},
     "List of per LoD sampling periods used in LoD generation.\n")
@@ -1204,20 +1202,13 @@ sanitizeEncoderOpts(
         attr_aps.lodSamplingPeriod[i] = attr_aps.lodSamplingPeriod[i - 1];
     }
 
-    // Set default threshold based on bitdepth
-    if (attr_aps.adaptive_prediction_threshold == -1) {
-      attr_aps.adaptive_prediction_threshold = 1 << (attr_sps.bitdepth - 2);
-    }
-
     if (attr_aps.attr_encoding == AttributeEncoding::kLiftingTransform) {
-      attr_aps.adaptive_prediction_threshold = 0;
       attr_aps.intra_lod_prediction_enabled_flag = false;
     }
 
     // For RAHT, ensure that the unused lod count = 0 (prevents mishaps)
     if (attr_aps.attr_encoding == AttributeEncoding::kRAHTransform) {
       attr_aps.num_detail_levels = 0;
-      attr_aps.adaptive_prediction_threshold = 0;
     }
 
     if (!params.encoder.gps.geom_angular_mode_enabled_flag) {
@@ -1369,11 +1360,6 @@ sanitizeEncoderOpts(
           err.error() << it.first << ".lodSamplingPeriod values must be > 1\n";
       }
 
-      if (attr_aps.adaptive_prediction_threshold < 0) {
-        err.error() << it.first
-                    << ".adaptivePredictionThreshold must be positive\n";
-      }
-
       if (
         attr_aps.num_pred_nearest_neighbours_minus1
         >= kAttributePredictionMaxNeighbourCount) {
diff --git a/tmc3/encoder.cpp b/tmc3/encoder.cpp
index 75d2851..9ae2b9f 100644
--- a/tmc3/encoder.cpp
+++ b/tmc3/encoder.cpp
@@ -579,7 +579,7 @@ PCCTMC3Encoder3::compressPartition(
 
     auto& ctxtMemAttr = _ctxtMemAttrs.at(abh.attr_sps_attr_idx);
     attrEncoder->encode(
-      *_sps, attr_sps, attr_aps, abh, ctxtMemAttr, pointCloud, &payload);
+      *_sps, attr_sps, attr_aps, abh, ctxtMemAttr, pointCloud, &payload, attr_enc);
 
     if (attr_aps.spherical_coord_flag)
       pointCloud.swapPoints(altPositions);
diff --git a/tmc3/hls.h b/tmc3/hls.h
index 11fe64d..b837043 100644
--- a/tmc3/hls.h
+++ b/tmc3/hls.h
@@ -558,7 +558,6 @@ struct AttributeParameterSet {
   bool canonical_point_order_flag;
   int num_pred_nearest_neighbours_minus1;
   int max_num_direct_predictors;
-  int adaptive_prediction_threshold;
   int intra_lod_search_range;
   int inter_lod_search_range;
 
diff --git a/tmc3/io_hls.cpp b/tmc3/io_hls.cpp
index 660b0d1..74330bc 100644
--- a/tmc3/io_hls.cpp
+++ b/tmc3/io_hls.cpp
@@ -850,8 +850,6 @@ write(const SequenceParameterSet& sps, const AttributeParameterSet& aps)
 
   if (aps.attr_encoding == AttributeEncoding::kPredictingTransform) {
     bs.writeUe(aps.max_num_direct_predictors);
-    if (aps.max_num_direct_predictors)
-      bs.writeUe(aps.adaptive_prediction_threshold);
     bs.write(aps.intra_lod_prediction_enabled_flag);
     bs.write(aps.inter_component_prediction_enabled_flag);
   }
@@ -936,9 +934,6 @@ parseAps(const PayloadBuffer& buf)
   aps.intra_lod_prediction_enabled_flag = false;
   if (aps.attr_encoding == AttributeEncoding::kPredictingTransform) {
     bs.readUe(&aps.max_num_direct_predictors);
-    aps.adaptive_prediction_threshold = 0;
-    if (aps.max_num_direct_predictors)
-      bs.readUe(&aps.adaptive_prediction_threshold);
     bs.read(&aps.intra_lod_prediction_enabled_flag);
     bs.read(&aps.inter_component_prediction_enabled_flag);
   }
