Skip to content

Commit

Permalink
#5710: Refactoring, move the getBasisTransformForNormal to texturelib…
Browse files Browse the repository at this point in the history
….h since we're going to need it outside the core module too.

Add some comments to the ComputeAxisBase method, since its purpose is not exactly obvious.
  • Loading branch information
codereader committed Sep 4, 2021
1 parent 2dfb14f commit a1f5207
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 41 deletions.
56 changes: 49 additions & 7 deletions libs/texturelib.h
Expand Up @@ -202,8 +202,24 @@ inline void Normal_GetTransform(const Vector3& normal, Matrix4& transform) {
transform[15] = 1;
}

/* greebo: This method calculates the normalised basis vectors of the texture plane as defined by <normal>
*
/* greebo: This method calculates the normalised texture basis vectors of the texture plane
* as defined by <normal>.
*
* Why is this needed? To calculate the texture coords of a brush winding,
* the texture projection matrix is used to transform the 3D vertex coords into 2D UV space.
* But *before* this happens the world system has to be rotated to align with
* the face's UV space. We only need the face normal to calculate the UV base vectors.
*
* There are two special cases coded into this method dealing with normals pointing
* straight up or straight down - in theses cases the world XY plane is
* rotated by 90 degrees.
*
* The idTech4 game engine is doing the same, even though the algorithm for the
* method "ComputeAxisBase" is implemented differently, based on atan2 instead
* of if-else and cross-products.
*
* The output basis vectors will be normalised.
*
* If the normal vector points to the z-direction, the basis vectors are part
* of the xy-plane: texS = <0,1,0> and texT = <1,0,0>
*
Expand All @@ -216,16 +232,17 @@ inline void Normal_GetTransform(const Vector3& normal, Matrix4& transform) {
*
* Note: the vector <normal> MUST be normalised for this to function correctly.
*/
inline void ComputeAxisBase(const Vector3& normal, Vector3& texS, Vector3& texT) {
const Vector3 up(0, 0, 1);
const Vector3 down(0, 0, -1);
inline void ComputeAxisBase(const Vector3& normal, Vector3& texS, Vector3& texT)
{
static const Vector3 up(0, 0, 1);
static const Vector3 down(0, 0, -1);

if (math::isNear(normal, up, 1e-6))
if (math::isNear(normal, up, 1e-6)) // straight up?
{
texS = Vector3(0, 1, 0);
texT = Vector3(1, 0, 0);
}
else if (math::isNear(normal, down, 1e-6))
else if (math::isNear(normal, down, 1e-6)) // straight down?
{
texS = Vector3(0, 1, 0);
texT = Vector3(-1, 0, 0);
Expand All @@ -238,6 +255,31 @@ inline void ComputeAxisBase(const Vector3& normal, Vector3& texS, Vector3& texT)
}
}

/* greebo: This returns the basis vectors of the texture (plane) space needed for projection.
* The vectors are normalised and stored within the basis matrix <basis>
* as column vectors.
*
* Note: the normal vector MUST be normalised already when this function is called,
* but this should be fulfilled as it represents a FacePlane vector (which is usually normalised)
*/
inline Matrix4 getBasisTransformForNormal(const Vector3& normal)
{
auto basis = Matrix4::getIdentity();

ComputeAxisBase(normal, basis.xCol().getVector3(), basis.yCol().getVector3());
basis.zCol().getVector3() = normal;

// At this point the basis matrix contains three column vectors that are
// perpendicular to each other.

// The x-line of <basis> contains the <texS> basis vector (within the face plane)
// The y-line of <basis> contains the <texT> basis vector (within the face plane)
// The z-line of <basis> contains the <normal> basis vector (perpendicular to the face plane)

// invert this matrix and return
return basis.getTransposed();
}

/* greebo: this is used to calculate the directions the patch is "flattened" in.
* If one of the patch bases is parallel or anti-parallel to the <faceNormal> it cannot
* be projected onto the facePlane, so a new orthogonal vector is taken as direction instead.
Expand Down
37 changes: 5 additions & 32 deletions radiantcore/brush/TextureProjection.cpp
Expand Up @@ -78,33 +78,6 @@ void TextureProjection::normalise(float width, float height) {
matrix.normalise(width, height);
}

/* greebo: This returns the basis vectors of the texture (plane) space.
* The vectors are normalised and stored within the basis matrix <basis>
* as line vectors.
*
* Note: the normal vector MUST be normalised already when this function is called,
* but this should be fulfilled as it represents a FacePlane vector (which is usually normalised)
*/
Matrix4 TextureProjection::getBasisForNormal(const Vector3& normal) const {

Matrix4 basis;

basis = Matrix4::getIdentity();
ComputeAxisBase(normal, basis.xCol().getVector3(), basis.yCol().getVector3());
basis.zCol().getVector3() = normal;

// At this point the basis matrix contains three lines that are
// perpendicular to each other.

// The x-line of <basis> contains the <texS> basis vector (within the face plane)
// The y-line of <basis> contains the <texT> basis vector (within the face plane)
// The z-line of <basis> contains the <normal> basis vector (perpendicular to the face plane)

basis.transpose();

return basis;
}

void TextureProjection::transformLocked(std::size_t width, std::size_t height, const Plane3& plane, const Matrix4& identity2transformed) {
//rMessage() << "identity2transformed: " << identity2transformed << "\n";

Expand All @@ -122,10 +95,10 @@ void TextureProjection::transformLocked(std::size_t width, std::size_t height, c

// stTransformed2stOriginal = stTransformed -> transformed -> identity -> stIdentity -> stOriginal

Matrix4 identity2stIdentity = getBasisForNormal(plane.normal());
Matrix4 identity2stIdentity = getBasisTransformForNormal(plane.normal());
//rMessage() << "identity2stIdentity: " << identity2stIdentity << "\n";

Matrix4 transformed2stTransformed = getBasisForNormal(normalTransformed);
Matrix4 transformed2stTransformed = getBasisTransformForNormal(normalTransformed);

Matrix4 stTransformed2identity(
transformed2stTransformed.getMultipliedBy(identity2transformed).getInverse()
Expand Down Expand Up @@ -187,7 +160,7 @@ void TextureProjection::fitTexture(std::size_t width, std::size_t height,
Matrix4 local2tex = st2tex;
{
Matrix4 xyz2st;
xyz2st = getBasisForNormal(normal);
xyz2st = getBasisTransformForNormal(normal);
local2tex.multiplyBy(xyz2st);
}

Expand Down Expand Up @@ -329,7 +302,7 @@ Matrix4 TextureProjection::getWorldToTexture(const Vector3& normal, const Matrix
// we don't care if it's not normalised...

// Retrieve the basis vectors of the texture plane space, they are perpendicular to <normal>
Matrix4 xyz2st = getBasisForNormal(localToWorld.transformDirection(normal));
Matrix4 xyz2st = getBasisTransformForNormal(localToWorld.transformDirection(normal));

// Transform the basis vectors with the according texture scale, rotate and shift operations
// These are contained in the local2tex matrix, so the matrices have to be multiplied.
Expand Down Expand Up @@ -367,7 +340,7 @@ void TextureProjection::emitTextureCoordinates(Winding& w, const Vector3& normal
// we don't care if it's not normalised...

// Retrieve the basis vectors of the texture plane space, they are perpendicular to <normal>
Matrix4 xyz2st = getBasisForNormal(localToWorld.transformDirection(normal));
Matrix4 xyz2st = getBasisTransformForNormal(localToWorld.transformDirection(normal));

// Transform the basis vectors with the according texture scale, rotate and shift operations
// These are contained in the local2tex matrix, so the matrices have to be multiplied.
Expand Down
2 changes: 0 additions & 2 deletions radiantcore/brush/TextureProjection.h
Expand Up @@ -46,8 +46,6 @@ class TextureProjection
// Normalise projection for a given texture width and height.
void normalise(float width, float height);

Matrix4 getBasisForNormal(const Vector3& normal) const;

void transformLocked(std::size_t width, std::size_t height, const Plane3& plane, const Matrix4& identity2transformed);

// Fits a texture to a brush face
Expand Down

0 comments on commit a1f5207

Please sign in to comment.