texturing brush

This commit is contained in:
sshumakov3
2021-05-11 00:41:00 +03:00
parent 7661878c59
commit 20e98f8bc9
17 changed files with 605 additions and 76 deletions

View File

@@ -75,8 +75,7 @@ int TextureSet::addTexture (scoped_blp_texture_reference texture)
if (tmp_edit_values && nTextures == 1)
{
// default values for the layer 0 are 255 (uint8) or 1.f (float) !
tmp_edit_values.get()[0].fill(1.f);
tmp_edit_values.get()[0].fill(255.f);
}
}
@@ -387,6 +386,291 @@ int TextureSet::get_texture_index_or_add (scoped_blp_texture_reference texture,
return addTexture (std::move (texture));
}
bool TextureSet::stampTexture(float xbase, float zbase, float x, float z, Brush* brush, float strength, float pressure, scoped_blp_texture_reference texture, QImage* image, bool paint)
{
bool changed = false;
float zPos, xPos, dist, radius;
int tex_layer = get_texture_index_or_add (std::move (texture), strength);
if (tex_layer == -1 || nTextures == 1)
{
return nTextures == 1;
}
radius = brush->getRadius();
if (misc::getShortestDist(x, z, xbase, zbase, CHUNKSIZE) > radius)
{
return changed;
}
create_temporary_alphamaps_if_needed();
auto& amaps = tmp_edit_values.get();
zPos = zbase;
for (int j = 0; j < 64; j++)
{
xPos = xbase;
for (int i = 0; i < 64; ++i)
{
if(std::abs(x - (xPos + TEXDETAILSIZE / 2.0)) > radius || std::abs(z - (zPos + TEXDETAILSIZE / 2.0f)) > radius)
{
xPos += TEXDETAILSIZE;
continue;
}
dist = misc::dist(x, z, xPos + TEXDETAILSIZE / 2.0f, zPos + TEXDETAILSIZE / 2.0f);
math::vector_3d const diff{math::vector_3d{xPos + TEXDETAILSIZE / 2.0f, 0.f, zPos + TEXDETAILSIZE / 2.0f} - math::vector_3d{x, 0.f, z}};
int pixel_x = std::floor(diff.x + radius);
int pixel_y = std::floor(diff.z + radius);
auto color = image->pixelColor(pixel_x, pixel_y);
float image_factor = (color.redF() + color.greenF() + color.blueF()) / 3.0f;
std::size_t offset = i + 64 * j;
// use double for more precision
std::array<double,4> alpha_values;
double total = 0.;
for (int n = 0; n < 4; ++n)
{
total += alpha_values[n] = amaps[n][i + 64 * j];
}
double current_alpha = alpha_values[tex_layer];
double sum_other_alphas = (total - current_alpha);
double alpha_change = image_factor * (strength - current_alpha) * pressure * brush->getValue(dist);
// alpha too low, set it to 0 directly
if (alpha_change < 0. && current_alpha + alpha_change < 1.)
{
alpha_change = -current_alpha;
}
if (misc::float_equals(current_alpha, strength))
{
xPos += TEXDETAILSIZE;
continue;
}
if (sum_other_alphas < 1.)
{
// alpha is currently at 254/255 -> set it at 255 and clear the rest of the values
if (alpha_change > 0.f)
{
for (int layer = 0; layer < nTextures; ++layer)
{
alpha_values[layer] = layer == tex_layer ? 255. : 0.f;
}
}
// all the other textures amount for less an 1/255 -> add the alpha_change (negative) to current texture and remove it from the first non current texture, clear the rest
else
{
bool change_applied = false;
for (int layer = 0; layer < nTextures; ++layer)
{
if (layer == tex_layer)
{
alpha_values[layer] += alpha_change;
}
else
{
if (!change_applied)
{
alpha_values[layer] -= alpha_change;
}
else
{
alpha_values[tex_layer] += alpha_values[layer];
alpha_values[layer] = 0.;
}
change_applied = true;
}
}
}
}
else
{
for (int layer = 0; layer < nTextures; ++layer)
{
if (layer == tex_layer)
{
alpha_values[layer] += alpha_change;
}
else
{
alpha_values[layer] -= alpha_change * alpha_values[layer] / sum_other_alphas;
// clear values too low to be visible
if (alpha_values[layer] < 1.)
{
alpha_values[tex_layer] += alpha_values[layer];
alpha_values[layer] = 0.f;
}
}
}
}
double total_final = std::accumulate(alpha_values.begin(), alpha_values.end(), 0.);
// failsafe in case the sum of all alpha values deviate
if (std::abs(total_final - 255.) > 0.001)
{
for (double& d : alpha_values)
{
d = d * 255. / total_final;
}
}
for (int n = 0; n < 4; ++n)
{
amaps[n][i + 64 * j] = static_cast<float>(alpha_values[n]);
}
changed = true;
xPos += TEXDETAILSIZE;
}
zPos += TEXDETAILSIZE;
}
if (!changed)
{
return false;
}
// cleanup
eraseUnusedTextures();
_need_amap_update = true;
_need_lod_texture_map_update = true;
return true;
/*
bool changed = false;
float zPos, xPos, dist, radius;
int tex_layer = get_texture_index_or_add (std::move (texture), strength);
if (tex_layer == -1 || nTextures == 1)
{
return nTextures == 1;
}
radius = brush->getRadius();
if (misc::getShortestDist(x, z, xbase, zbase, CHUNKSIZE) > radius)
{
return changed;
}
create_temporary_alphamaps_if_needed();
auto& amaps = tmp_edit_values.get();
zPos = zbase;
for (int j = 0; j < 64; j++)
{
xPos = xbase;
for (int i = 0; i < 64; ++i)
{
if(std::abs(x - (xPos + TEXDETAILSIZE / 2.0)) > radius || std::abs(z - (zPos + TEXDETAILSIZE / 2.0f)) > radius)
continue;
dist = misc::dist(x, z, xPos + TEXDETAILSIZE / 2.0f, zPos + TEXDETAILSIZE / 2.0f);
math::vector_3d const diff{math::vector_3d{xPos + TEXDETAILSIZE / 2.0f, 0.f, zPos + TEXDETAILSIZE / 2.0f} - math::vector_3d{x, 0.f, z}};
int pixel_x = std::floor(diff.x + radius);
int pixel_y = std::floor(diff.z + radius);
auto color = image->pixelColor(pixel_x, pixel_y);
float image_factor = (color.redF() + color.greenF() + color.blueF()) / 3.0f;
std::size_t offset = i + 64 * j;
float current_alpha = amaps[tex_layer][i + 64 * j];
float sum_other_alphas = 1.f - current_alpha;
float alpha_change = image_factor * ((strength - current_alpha) * pressure * brush->getValue(dist));
if (misc::float_equals(current_alpha, strength))
continue;
float totOthers = 0.0f;
for (int layer = 0; layer < nTextures; ++layer)
{
if (layer == tex_layer)
{
amaps[layer][offset] += alpha_change;
}
else
{
amaps[layer][offset] -= alpha_change * (amaps[layer][offset] / sum_other_alphas);
}
if (amaps[layer][offset] > 1.0f)
amaps[layer][offset] = 1.0f;
else if (amaps[layer][offset] < 0.0f || isnan(amaps[layer][offset]))
amaps[layer][offset] = 0.0f;
if(layer != 0)
totOthers += amaps[layer][offset];
}
if (totOthers > 1)
{
float mul = (1 / totOthers);
amaps[1][offset] = amaps[1][offset] * mul;
totOthers = amaps[1][offset];
if (nTextures > 2)
{
amaps[2][offset] = amaps[2][offset] * mul;
totOthers += amaps[2][offset];
}
if (nTextures > 3)
{
amaps[3][offset] = amaps[3][offset] * mul;
totOthers += amaps[3][offset];
}
}
amaps[0][offset] = std::max(0.0f, std::min(1.0f - totOthers, 1.0f));
changed = true;
xPos += TEXDETAILSIZE;
}
zPos += TEXDETAILSIZE;
}
if (!changed)
{
return false;
}
// cleanup
eraseUnusedTextures();
_need_amap_update = true;
_need_lod_texture_map_update = true;
return true;
*/
}
bool TextureSet::paintTexture(float xbase, float zbase, float x, float z, Brush* brush, float strength, float pressure, scoped_blp_texture_reference texture)
{
bool changed = false;
@@ -419,27 +703,105 @@ bool TextureSet::paintTexture(float xbase, float zbase, float x, float z, Brush*
{
dist = misc::dist(x, z, xPos + TEXDETAILSIZE / 2.0f, zPos + TEXDETAILSIZE / 2.0f);
if (dist<=radius)
if (dist <= radius)
{
std::size_t offset = i + 64 * j;
// use double for more precision
std::array<double,4> alpha_values;
double total = 0.;
float current_alpha = amaps[tex_layer][i + 64 * j];
float sum_other_alphas = 1.f - current_alpha;
float alpha_change = (strength - current_alpha)* pressure * brush->getValue(dist);
for (int n = 0; n < 4; ++n)
{
total += alpha_values[n] = amaps[n][i + 64 * j];
}
double current_alpha = alpha_values[tex_layer];
double sum_other_alphas = (total - current_alpha);
double alpha_change = (strength - current_alpha) * pressure * brush->getValue(dist);
// alpha too low, set it to 0 directly
if (alpha_change < 0. && current_alpha + alpha_change < 1.)
{
alpha_change = -current_alpha;
}
if (!misc::float_equals(current_alpha, strength))
{
for (int layer = 0; layer < nTextures; ++layer)
if (sum_other_alphas < 1.)
{
if (layer == tex_layer)
// alpha is currently at 254/255 -> set it at 255 and clear the rest of the values
if (alpha_change > 0.f)
{
amaps[layer][offset] += alpha_change;
for (int layer = 0; layer < nTextures; ++layer)
{
alpha_values[layer] = layer == tex_layer ? 255. : 0.f;
}
}
// all the other textures amount for less an 1/255 -> add the alpha_change (negative) to current texture and remove it from the first non current texture, clear the rest
else
{
amaps[layer][offset] -= alpha_change * (amaps[layer][offset] / sum_other_alphas);
bool change_applied = false;
for (int layer = 0; layer < nTextures; ++layer)
{
if (layer == tex_layer)
{
alpha_values[layer] += alpha_change;
}
else
{
if (!change_applied)
{
alpha_values[layer] -= alpha_change;
}
else
{
alpha_values[tex_layer] += alpha_values[layer];
alpha_values[layer] = 0.;
}
change_applied = true;
}
}
}
}
else
{
for (int layer = 0; layer < nTextures; ++layer)
{
if (layer == tex_layer)
{
alpha_values[layer] += alpha_change;
}
else
{
alpha_values[layer] -= alpha_change * alpha_values[layer] / sum_other_alphas;
// clear values too low to be visible
if (alpha_values[layer] < 1.)
{
alpha_values[tex_layer] += alpha_values[layer];
alpha_values[layer] = 0.f;
}
}
}
}
double total_final = std::accumulate(alpha_values.begin(), alpha_values.end(), 0.);
// failsafe in case the sum of all alpha values deviate
if (std::abs(total_final - 255.) > 0.001)
{
for (double& d : alpha_values)
{
d = d * 255. / total_final;
}
}
for (int n = 0; n < 4; ++n)
{
amaps[n][i + 64 * j] = static_cast<float>(alpha_values[n]);
}
changed = true;
}
@@ -840,7 +1202,7 @@ void TextureSet::bind_alpha(std::size_t id)
{
for (int alpha_id = 0; alpha_id < 3; ++alpha_id)
{
amap[i * 3 + alpha_id] = tmp_amaps[alpha_id+1][i];
amap[i * 3 + alpha_id] = tmp_amaps[alpha_id + 1][i] / 255.f;
}
}
@@ -970,6 +1332,8 @@ bool TextureSet::apply_alpha_changes()
}
auto& new_amaps = tmp_edit_values.get();
std::array<std::uint16_t, 64 * 64> totals;
totals.fill(0);
for (int alpha_layer = 0; alpha_layer < nTextures - 1; ++alpha_layer)
{
@@ -977,7 +1341,15 @@ bool TextureSet::apply_alpha_changes()
for (int i = 0; i < 64 * 64; ++i)
{
values[i] = float_alpha_to_uint8(new_amaps[alpha_layer + 1][i] * 255.f);
values[i] = float_alpha_to_uint8(new_amaps[alpha_layer + 1][i]);
totals[i] += values[i];
// remove the possible overflow with rounding
// max 2 if all 4 values round up so it won't change the layer's alpha much
if (totals[i] > 255)
{
values[i] -= static_cast<std::uint8_t>(totals[i] - 255);
}
}
alphamaps[alpha_layer]->setAlpha(values.data());
@@ -1004,11 +1376,11 @@ void TextureSet::create_temporary_alphamaps_if_needed()
for (int i = 0; i < 64 * 64; ++i)
{
float base_alpha = 1.f;
float base_alpha = 255.f;
for (int alpha_layer = 0; alpha_layer < nTextures-1; ++alpha_layer)
for (int alpha_layer = 0; alpha_layer < nTextures - 1; ++alpha_layer)
{
float f = static_cast<float>(alphamaps[alpha_layer]->getAlpha(i)) / 255.f;
float f = static_cast<float>(alphamaps[alpha_layer]->getAlpha(i));
values[alpha_layer + 1][i] = f;
base_alpha -= f;