texturing brush
This commit is contained in:
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user