godot_voxel/edition/funcs.h

114 lines
3.4 KiB
C++

#ifndef EDITION_FUNCS_H
#define EDITION_FUNCS_H
#include "../storage/funcs.h"
#include "../util/fixed_array.h"
namespace zylann::voxel {
inline void _normalize_weights_preserving(FixedArray<float, 4> &weights, unsigned int preserved_index,
unsigned int other0, unsigned int other1, unsigned int other2) {
const float part_sum = weights[other0] + weights[other1] + weights[other2];
// It is assumed the preserved channel is already clamped to [0, 1]
const float expected_part_sum = 1.f - weights[preserved_index];
if (part_sum < 0.0001f) {
weights[other0] = expected_part_sum / 3.f;
weights[other1] = expected_part_sum / 3.f;
weights[other2] = expected_part_sum / 3.f;
} else {
const float scale = expected_part_sum / part_sum;
weights[other0] *= scale;
weights[other1] *= scale;
weights[other2] *= scale;
}
}
inline void normalize_weights_preserving(FixedArray<float, 4> &weights, unsigned int preserved_index) {
switch (preserved_index) {
case 0:
_normalize_weights_preserving(weights, 0, 1, 2, 3);
break;
case 1:
_normalize_weights_preserving(weights, 1, 0, 2, 3);
break;
case 2:
_normalize_weights_preserving(weights, 2, 1, 0, 3);
break;
default:
_normalize_weights_preserving(weights, 3, 1, 2, 0);
break;
}
}
/*inline void normalize_weights(FixedArray<float, 4> &weights) {
float sum = 0;
for (unsigned int i = 0; i < weights.size(); ++i) {
sum += weights[i];
}
const float sum_inv = 255.f / sum;
for (unsigned int i = 0; i < weights.size(); ++i) {
weights[i] = clamp(weights[i] * sum_inv, 0.f, 255.f);
}
}*/
inline void blend_texture_packed_u16(
int texture_index, float target_weight, uint16_t &encoded_indices, uint16_t &encoded_weights) {
#ifdef DEBUG_ENABLED
ERR_FAIL_COND(target_weight < 0.f || target_weight > 1.f);
#endif
FixedArray<uint8_t, 4> indices = decode_indices_from_packed_u16(encoded_indices);
FixedArray<uint8_t, 4> weights = decode_weights_from_packed_u16(encoded_weights);
// Search if our texture index is already present
unsigned int component_index = 4;
for (unsigned int i = 0; i < indices.size(); ++i) {
if (indices[i] == texture_index) {
component_index = i;
break;
}
}
bool index_was_changed = false;
if (component_index >= indices.size()) {
// Our texture index is not present, we'll replace the lowest weight
uint8_t lowest_weight = 255;
// Default to 0 in the hypothetic case where all weights are maxed
component_index = 0;
for (unsigned int i = 0; i < weights.size(); ++i) {
if (weights[i] < lowest_weight) {
lowest_weight = weights[i];
component_index = i;
}
}
indices[component_index] = texture_index;
index_was_changed = true;
}
// TODO Optimization in case target_weight is 1?
FixedArray<float, 4> weights_f;
for (unsigned int i = 0; i < weights.size(); ++i) {
weights_f[i] = weights[i] / 255.f;
}
if (weights_f[component_index] < target_weight || index_was_changed) {
weights_f[component_index] = target_weight;
normalize_weights_preserving(weights_f, component_index);
for (unsigned int i = 0; i < weights_f.size(); ++i) {
weights[i] = math::clamp(weights_f[i] * 255.f, 0.f, 255.f);
}
encoded_indices = encode_indices_to_packed_u16(indices[0], indices[1], indices[2], indices[3]);
encoded_weights = encode_weights_to_packed_u16(weights[0], weights[1], weights[2], weights[3]);
}
}
} // namespace zylann::voxel
#endif // EDITION_FUNCS_H