I'm using an engine that implements a texture mapping technique where the texture map for each triangle is defined by a point(P) and 2 vectors(M, N) instead of per-vertex texture coordinates P defines the origin of the texture, M defines the horizontal end of the texture and N the vertical end The technique is better explained here: https://nothings.org/gamedev/ray_plane.html
Now, given a triangle ABC and a UV for each vertex of the triangle i needed to compute the PMN values for those UV coordinates, thankfully an answer that describes an algorithm from UV -> PMN already exists: https://gamedev.stackexchange.com/a/185429/147479
However i am now wondering if it's possible to construct different combinations of PMN(that would yield a correct result, small error margin is fine and expected)
Example using the UV -> PMN algorithm: the triangle ABC with positions {20, -75, -40) {-30, 15, 60} {-5, 26, 63} and UV coordinates {0.2, 0.4} {0.2, 0.9} {0.8, 0.5} could yield the PMN coordinates {30, -75, 16} {32, -37, 90} {4, -13, -80} and the error rate could be 0.0005(which is acceptable) the error rate can be determined by running the UV -> PMN algorithm, then converting the constructed PMN coordinates back to UV(algorithm described in the question i linked) and comparing the two (original UV's and the constructed ones from doing UV -> PMN -> UV)
A different combination could be something like {110, 54, -15} {44, -82, 16} {-14, -102, 130} it just has to match(small error margin is fine) the original UV coordinates (if it was converted back to UV coordinates)
Implementation of the algorithm DMGregory proposed:
Vector3D pPrime = new Vector3D(65, -35, 77); // arbitrary point i picked
while (true) {
Matrix4d mat = new Matrix4d(
p.getX(), m.getX(), n.getX(), pPrime.getX(),
p.getY(), m.getY(), n.getY(), pPrime.getY(),
p.getZ(), m.getZ(), n.getZ(), pPrime.getZ(),
1, 1, 1, 1
);
//determine whether 4 points lie on the same plane or not
if (mat.determinant() > 0.001) { // check against epsilon just in case
break;
}
double x = ThreadLocalRandom.current().nextInt(-200, 200);
double y = ThreadLocalRandom.current().nextInt(-200, 200);
double z = ThreadLocalRandom.current().nextInt(-200, 200);
pPrime = new Vector3D(x, y, z);
}
Vector3D d = divide(pPrime.subtract(p), pPrime.subtract(p).getNorm());
Vector3D mPrime = pPrime.add(m.subtract(p))
.subtract(d.scalarMultiply(m.subtract(p).dotProduct(d)));
Vector3D nPrime = pPrime.add(n.subtract(p))
.subtract(d.scalarMultiply(n.subtract(p).dotProduct(d)));
Picking M, N values close to whole numbers:
float eps = 0.07f;
int i = 1;
while (true) {
List<Vector3D> newPMN = computeNewPMN(p, m, n);
Vector3D pPrime = newPMN.get(0);
Vector3D mPrime = newPMN.get(1);
Vector3D nPrime = newPMN.get(2);
double fractMX = mPrime.getX() % 1;
double fractMY = mPrime.getY() % 1;
double fractMZ = mPrime.getZ() % 1;
double fractNX = nPrime.getX() % 1;
double fractNY = nPrime.getY() % 1;
double fractNZ = nPrime.getZ() % 1;
boolean closeTo0 = abs(fractMX) < eps && abs(fractMY) < eps && abs(fractMZ) < eps && abs(fractNX) < eps && abs(fractNY) < eps && abs(fractNZ) < eps;
float eps1 = 1f - eps;
boolean closeTo1 = abs(fractMX) > eps1 && abs(fractMY) > eps1 && abs(fractMZ) > eps1 && abs(fractNX) > eps1 && abs(fractNY) > eps1 && abs(fractNZ) > eps1;
if (closeTo0 || closeTo1) {
System.out.println("Found valid at " + i);
// do stuff with pPrime, mPrime, nPrime
break;
}
i++;
}
where computeNewPMN
contains the code mentioned above