Skip to content

Commit

Permalink
#5771: Simpler, but more effective algorithm, wrapping the texture of…
Browse files Browse the repository at this point in the history
… the source face around the shared edge, extrapolating it.
  • Loading branch information
codereader committed Oct 3, 2021
1 parent 99bd25c commit 59a5fc0
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 70 deletions.
105 changes: 36 additions & 69 deletions radiantcore/brush/Face.cpp
Expand Up @@ -529,18 +529,6 @@ void Face::applyShaderFromFace(const Face& other)
{
undoSave();

rMessage() << "Other Winding: " << std::endl;
for (std::size_t i = 0; i < other.m_winding.size(); ++i)
{
rMessage() << i << ": XYZ=" << other.m_winding[i].vertex << ", UV=" << other.m_winding[i].texcoord << std::endl;
}

rMessage() << "This Winding: " << std::endl;
for (std::size_t i = 0; i < m_winding.size(); ++i)
{
rMessage() << i << ": XYZ=" << m_winding[i].vertex << ", UV=" << m_winding[i].texcoord << std::endl;
}

// Apply the material of the other face
setShader(other.getShader());

Expand All @@ -549,8 +537,7 @@ void Face::applyShaderFromFace(const Face& other)
other.GetTexdef(projection);

// The list of shared vertices (other face index => this face index)
using SharedVertexIndices = std::vector<std::pair<std::size_t, std::size_t>>;
SharedVertexIndices sharedVertices;
std::vector<std::pair<std::size_t, std::size_t>> sharedVertices;

// Let's see whether this face is sharing any 3D coordinates with the other one
for (std::size_t i = 0; i < other.m_winding.size(); ++i)
Expand All @@ -567,69 +554,49 @@ void Face::applyShaderFromFace(const Face& other)
}
}

SetTexdef(projection);

if (sharedVertices.empty())
{
return; // nothing more to do
}

// Apply this texture matrix to all our winding vertices to be able to measure distances
emitTextureCoordinates();

// Pick one or two non-shared vertices to calculate the distance in UV space
// When shifting the texture we want to keep that distance intact
std::vector<std::pair<std::size_t, Vector2>> uvDistanceToNonshared;

// First shared vertex of this face
auto firstSharedVertex = sharedVertices[0].second;

// If we have two shared vertices, calculate the UV distance to one more vertex
for (std::size_t j = 0; j < m_winding.size(); ++j)
// Do we have a shared edge?
if (sharedVertices.size() == 2)
{
if (std::find_if(sharedVertices.begin(), sharedVertices.end(), [&](const SharedVertexIndices::value_type& indexPair)
{ return indexPair.second == j; }) != sharedVertices.end())
// We wrap the texture around the shared edge, check the UV scale perpendicular to that edge
auto edgeCenter = (other.m_winding[sharedVertices[0].first].vertex + other.m_winding[sharedVertices[1].first].vertex) * 0.5;
auto centroidToEdgeCenter = edgeCenter - other.m_centroid;

// Pick a point outside face, following the ray from the centroid through the edge center
auto extrapolatedPoint = edgeCenter + centroidToEdgeCenter;
auto extrapolationLength = centroidToEdgeCenter.getLength();
auto extrapolatedTexcoords = other.m_texdefTransformed.getTextureCoordsForVertex(
extrapolatedPoint, other.m_planeTransformed.getPlane().normal(), Matrix4::getIdentity()
);

// Both faces share the same edge center
// Calculate a point on this face plane, with the same distance from the edge center
auto pointOnThisFacePlane = edgeCenter + (m_centroid - edgeCenter).getNormalised() * extrapolationLength;

// Now we have 3 vertices and 3 texcoords to calculate the matching texdef
Vector3 vertices[3] =
{
continue; // this is a shared vertex
}
m_winding[sharedVertices[0].second].vertex,
m_winding[sharedVertices[1].second].vertex,
pointOnThisFacePlane
};

// Remember this distance
uvDistanceToNonshared.emplace_back(j, m_winding[j].texcoord - m_winding[firstSharedVertex].texcoord);
}
// Use the shared texcoords we found on the other face, and the third one we calculated
Vector2 texcoords[3] =
{
other.m_winding[sharedVertices[0].first].texcoord,
other.m_winding[sharedVertices[1].first].texcoord,
extrapolatedTexcoords
};

if (uvDistanceToNonshared.empty())
{
setTexDefFromPoints(vertices, texcoords);
_texdef = m_texdefTransformed; // freeze that matrix
return;
}

// Assemble three vertices we can calculate the shift-corrected texture matrix from
Vector3 vertices[3];
Vector2 texcoords[3];

if (sharedVertices.size() == 2)
else
{
// Two vertices are shared anyway
vertices[0] = m_winding[sharedVertices[0].second].vertex;
vertices[1] = m_winding[sharedVertices[1].second].vertex;

// Use the texcoord of the other face
texcoords[0] = other.m_winding[sharedVertices[0].first].texcoord;
texcoords[1] = other.m_winding[sharedVertices[1].first].texcoord;

// The third vertex is a non-shared one
// Use the world coordinates of this face
vertices[2] = m_winding[uvDistanceToNonshared[0].first].vertex;
// The texture coordinate will have the same distance as before the fix
texcoords[2] = texcoords[0] + uvDistanceToNonshared[0].second;
// Just use the other projection, as-is
SetTexdef(projection);
}
else if (sharedVertices.size() == 1)
{
rWarning() << "Algorithm for 1 shared vertex not implemented";
return;
}

setTexDefFromPoints(vertices, texcoords);
_texdef = m_texdefTransformed;
}

void Face::setTexDefFromPoints(const Vector3 points[3], const Vector2 uvs[3])
Expand Down
12 changes: 11 additions & 1 deletion radiantcore/brush/TextureProjection.cpp
Expand Up @@ -264,7 +264,8 @@ void TextureProjection::alignTexture(IFace::AlignEdge align, const Winding& wind
shift(-delta.x(), delta.y());
}

Matrix4 TextureProjection::getWorldToTexture(const Vector3& normal, const Matrix4& localToWorld) const {
Matrix4 TextureProjection::getWorldToTexture(const Vector3& normal, const Matrix4& localToWorld) const
{
// Get the transformation matrix, that contains the shift, scale and rotation
// of the texture in "condensed" form (as matrix components).
Matrix4 local2tex = getTransform();
Expand Down Expand Up @@ -343,3 +344,12 @@ void TextureProjection::emitTextureCoordinates(Winding& w, const Vector3& normal
i->bitangent = bitangent;
}
}

Vector2 TextureProjection::getTextureCoordsForVertex(const Vector3& point, const Vector3& normal, const Matrix4& localToWorld) const
{
auto local2tex = getWorldToTexture(normal, localToWorld);

auto texcoord = local2tex.transformPoint(point);

return { texcoord.x(), texcoord.y() };
}
3 changes: 3 additions & 0 deletions radiantcore/brush/TextureProjection.h
Expand Up @@ -57,6 +57,9 @@ class TextureProjection
// greebo: Saves the texture definitions into the brush winding points
void emitTextureCoordinates(Winding& w, const Vector3& normal, const Matrix4& localToWorld) const;

// Calculates the UV coords of a single point
Vector2 getTextureCoordsForVertex(const Vector3& point, const Vector3& normal, const Matrix4& localToWorld) const;

// greebo: This returns a matrix transforming world vertex coordinates into texture space
Matrix4 getWorldToTexture(const Vector3& normal, const Matrix4& localToWorld) const;

Expand Down

0 comments on commit 59a5fc0

Please sign in to comment.