diff --git a/PS1BSP.vcxproj b/PS1BSP.vcxproj
index 4a5ccb1..1452283 100644
--- a/PS1BSP.vcxproj
+++ b/PS1BSP.vcxproj
@@ -153,6 +153,7 @@
+
diff --git a/PS1BSP.vcxproj.filters b/PS1BSP.vcxproj.filters
index b514ea6..eb8a372 100644
--- a/PS1BSP.vcxproj.filters
+++ b/PS1BSP.vcxproj.filters
@@ -83,6 +83,9 @@
Header Files
+
+ Header Files
+
diff --git a/bsp.h b/bsp.h
index 97447e0..fe8089d 100644
--- a/bsp.h
+++ b/bsp.h
@@ -71,6 +71,12 @@ typedef struct Plane
tangent = normal.crossProduct(dir).normalized();
bitangent = tangent.crossProduct(normal);
}
+
+ Vec3 projectPoint(const Vec3& point) const
+ {
+ double pointDist = normal.dotProduct(point) - dist;
+ return point - normal * pointDist;
+ }
} plane_t;
typedef struct BoundBox // Bounding Box, Float values
diff --git a/matrix.h b/matrix.h
new file mode 100644
index 0000000..d42d2ab
--- /dev/null
+++ b/matrix.h
@@ -0,0 +1,172 @@
+#pragma once
+
+class Matrix4x4
+{
+public:
+ union
+ {
+ double m[16];
+ double _m[4][4];
+ };
+
+ Matrix4x4() { LoadIdentity(); }
+
+ void LoadNull()
+ {
+ for (int i = 0; i < 16; i++)
+ m[i] = 0;
+ }
+
+ void LoadIdentity()
+ {
+ m[0] = m[5] = m[10] = m[15] = 1;
+ m[1] = m[2] = m[3] = m[4] =
+ m[6] = m[7] = m[8] = m[9] =
+ m[11] = m[12] = m[13] = m[14] = 0;
+ }
+
+ void SetAxis(int axis, const Vec3& value)
+ {
+ m[0 + axis] = value.x;
+ m[4 + axis] = value.y;
+ m[8 + axis] = value.z;
+ }
+
+ void SetTranslation(const Vec3& value)
+ {
+ m[12] = value.x;
+ m[13] = value.y;
+ m[14] = value.z;
+ }
+
+ Vec3 TransformPoint(const Vec3& point)
+ {
+ return Vec3(
+ (double)point.x * m[0] +
+ (double)point.y * m[4] +
+ (double)point.z * m[8] +
+ m[12],
+
+ (double)point.x * m[1] +
+ (double)point.y * m[5] +
+ (double)point.z * m[9] +
+ m[13],
+
+ (double)point.x * m[2] +
+ (double)point.y * m[6] +
+ (double)point.z * m[10] +
+ m[14]);
+ }
+
+ Vec3 TransformDirection(const Vec3& dir)
+ {
+ return Vec3(
+ (double)dir.x * m[0] +
+ (double)dir.y * m[4] +
+ (double)dir.z * m[8],
+
+ (double)dir.x * m[1] +
+ (double)dir.y * m[5] +
+ (double)dir.z * m[9],
+
+ (double)dir.x * m[2] +
+ (double)dir.y * m[6] +
+ (double)dir.z * m[10]);
+ }
+
+ bool Invert()
+ {
+ double tmp[12];
+ double src[16];
+ double dst[16];
+
+ // Transpose matrix
+ for (int i = 0; i < 4; i++) {
+ src[i + 0] = m[i * 4 + 0];
+ src[i + 4] = m[i * 4 + 1];
+ src[i + 8] = m[i * 4 + 2];
+ src[i + 12] = m[i * 4 + 3];
+ }
+
+ // Calculate pairs for first 8 elements (cofactors)
+ tmp[0] = src[10] * src[15];
+ tmp[1] = src[11] * src[14];
+ tmp[2] = src[9] * src[15];
+ tmp[3] = src[11] * src[13];
+ tmp[4] = src[9] * src[14];
+ tmp[5] = src[10] * src[13];
+ tmp[6] = src[8] * src[15];
+ tmp[7] = src[11] * src[12];
+ tmp[8] = src[8] * src[14];
+ tmp[9] = src[10] * src[12];
+ tmp[10] = src[8] * src[13];
+ tmp[11] = src[9] * src[12];
+
+ // Calculate first 8 elements (cofactors)
+ dst[0] = tmp[0] * src[5] + tmp[3] * src[6] + tmp[4] * src[7];
+ dst[0] -= tmp[1] * src[5] + tmp[2] * src[6] + tmp[5] * src[7];
+ dst[1] = tmp[1] * src[4] + tmp[6] * src[6] + tmp[9] * src[7];
+ dst[1] -= tmp[0] * src[4] + tmp[7] * src[6] + tmp[8] * src[7];
+ dst[2] = tmp[2] * src[4] + tmp[7] * src[5] + tmp[10] * src[7];
+ dst[2] -= tmp[3] * src[4] + tmp[6] * src[5] + tmp[11] * src[7];
+ dst[3] = tmp[5] * src[4] + tmp[8] * src[5] + tmp[11] * src[6];
+ dst[3] -= tmp[4] * src[4] + tmp[9] * src[5] + tmp[10] * src[6];
+ dst[4] = tmp[1] * src[1] + tmp[2] * src[2] + tmp[5] * src[3];
+ dst[4] -= tmp[0] * src[1] + tmp[3] * src[2] + tmp[4] * src[3];
+ dst[5] = tmp[0] * src[0] + tmp[7] * src[2] + tmp[8] * src[3];
+ dst[5] -= tmp[1] * src[0] + tmp[6] * src[2] + tmp[9] * src[3];
+ dst[6] = tmp[3] * src[0] + tmp[6] * src[1] + tmp[11] * src[3];
+ dst[6] -= tmp[2] * src[0] + tmp[7] * src[1] + tmp[10] * src[3];
+ dst[7] = tmp[4] * src[0] + tmp[9] * src[1] + tmp[10] * src[2];
+ dst[7] -= tmp[5] * src[0] + tmp[8] * src[1] + tmp[11] * src[2];
+
+ // Calculate pairs for second 8 elements (cofactors)
+ tmp[0] = src[2] * src[7];
+ tmp[1] = src[3] * src[6];
+ tmp[2] = src[1] * src[7];
+ tmp[3] = src[3] * src[5];
+ tmp[4] = src[1] * src[6];
+ tmp[5] = src[2] * src[5];
+ tmp[6] = src[0] * src[7];
+ tmp[7] = src[3] * src[4];
+ tmp[8] = src[0] * src[6];
+ tmp[9] = src[2] * src[4];
+ tmp[10] = src[0] * src[5];
+ tmp[11] = src[1] * src[4];
+
+ // Calculate second 8 elements (cofactors)
+ dst[8] = tmp[0] * src[13] + tmp[3] * src[14] + tmp[4] * src[15];
+ dst[8] -= tmp[1] * src[13] + tmp[2] * src[14] + tmp[5] * src[15];
+ dst[9] = tmp[1] * src[12] + tmp[6] * src[14] + tmp[9] * src[15];
+ dst[9] -= tmp[0] * src[12] + tmp[7] * src[14] + tmp[8] * src[15];
+ dst[10] = tmp[2] * src[12] + tmp[7] * src[13] + tmp[10] * src[15];
+ dst[10] -= tmp[3] * src[12] + tmp[6] * src[13] + tmp[11] * src[15];
+ dst[11] = tmp[5] * src[12] + tmp[8] * src[13] + tmp[11] * src[14];
+ dst[11] -= tmp[4] * src[12] + tmp[9] * src[13] + tmp[10] * src[14];
+ dst[12] = tmp[2] * src[10] + tmp[5] * src[11] + tmp[1] * src[9];
+ dst[12] -= tmp[4] * src[11] + tmp[0] * src[9] + tmp[3] * src[10];
+ dst[13] = tmp[8] * src[11] + tmp[0] * src[8] + tmp[7] * src[10];
+ dst[13] -= tmp[6] * src[10] + tmp[9] * src[11] + tmp[1] * src[8];
+ dst[14] = tmp[6] * src[9] + tmp[11] * src[11] + tmp[3] * src[8];
+ dst[14] -= tmp[10] * src[11] + tmp[2] * src[8] + tmp[7] * src[9];
+ dst[15] = tmp[10] * src[10] + tmp[4] * src[8] + tmp[9] * src[9];
+ dst[15] -= tmp[8] * src[9] + tmp[11] * src[10] + tmp[5] * src[8];
+
+ // Calculate determinant
+ double det = src[0] * dst[0] + src[1] * dst[1] + src[2] * dst[2] + src[3] * dst[3];
+
+ if (fabs(det) < FLT_EPSILON)
+ {
+ return false;
+ }
+ else
+ {
+ // Calculate matrix inverse
+ det = 1.0 / det;
+ for (int i = 0; i < 16; i++)
+ m[i] = dst[i] * det;
+ }
+
+ return true;
+ }
+};
diff --git a/tesselate.cpp b/tesselate.cpp
index 4c647fa..691211c 100644
--- a/tesselate.cpp
+++ b/tesselate.cpp
@@ -2,6 +2,7 @@
#include "bsp.h"
#include "tesselate.h"
#include "gpc.h"
+#include "matrix.h"
std::vector Tesselator::tesselateFace(const face_t* face)
{
@@ -11,9 +12,6 @@ std::vector Tesselator::tesselateFace(const face_t* face)
const miptex_t* miptex = &world->miptexes[texinfo->texture_id];
const plane_t* plane = &world->planes[face->plane_id];
- double minS = DBL_MAX, minT = DBL_MAX;
- double maxS = DBL_MIN, maxT = DBL_MIN;
-
gpc_polygon facePolygon = { 0 };
gpc_vertex_list contour;
contour.num_vertices = face->ledge_num;
@@ -21,8 +19,17 @@ std::vector Tesselator::tesselateFace(const face_t* face)
if (contour.vertex == NULL)
return polygons;
- double invSLenSqr = 1.0 / texinfo->vectorS.sqrMagnitude();
- double invTLenSqr = 1.0 / texinfo->vectorT.sqrMagnitude();
+ double minS = DBL_MAX, minT = DBL_MAX;
+ double maxS = DBL_MIN, maxT = DBL_MIN;
+
+ // vectorS and vectorT are normally perpendicular (dot product is 0), magnitude isn't always 1 but that's fine.
+ // Means we can construct a coordinate space from them (plane normal for the third vector) and transform the vertices to texture space.
+ // And we can create an inverse transform, meaning we can transform vertices from 2D texture space back to 3D world space.
+ Matrix4x4 textureTrsf;
+ textureTrsf.SetAxis(0, texinfo->vectorS / (float)miptex->width);
+ textureTrsf.SetAxis(1, texinfo->vectorT / (float)miptex->height);
+ textureTrsf.SetAxis(2, plane->normal);
+ textureTrsf.SetTranslation(Vec3(texinfo->distS / miptex->width, texinfo->distT / miptex->height, -plane->dist));
// Build a polygon in normalized 2D texture space from the original face data
std::vector faceVertices;
@@ -38,22 +45,22 @@ std::vector Tesselator::tesselateFace(const face_t* face)
Vec3 vertexPoint = vertex->toVec();
faceVertices.push_back(vertexPoint);
- // vectorS and vectorT are normally perpendicular (dot product is 0), magnitude isn't always 1 but that's fine
- // Means we can construct a coordinate space from them (cross product for the third vector) and transform the vertex point to ST-space
- // And we can create an inverse transform... though just having s and t values probably isn't enough to completely transform back...
+ // Transform the vertex to texture space and calculate the texture UV bounds
+ Vec3 st = textureTrsf.TransformPoint(vertexPoint);
+ if (st.x > maxS) maxS = st.x; if (st.x < minS) minS = st.x;
+ if (st.y > maxT) maxT = st.y; if (st.y < minT) minT = st.y;
- // Calculate texture UV bounds
- double s = (vertexPoint.dotProduct(texinfo->vectorS) + texinfo->distS) / miptex->width;
- double t = (vertexPoint.dotProduct(texinfo->vectorT) + texinfo->distT) / miptex->height;
- if (s > maxS) maxS = s; if (s < minS) minS = s;
- if (t > maxT) maxT = t; if (t < minT) minT = t;
-
- contour.vertex[edgeListIdx] = gpc_vertex{ s, t };
+ contour.vertex[edgeListIdx] = gpc_vertex{ st.x, st.y };
}
gpc_add_contour(&facePolygon, &contour, 0);
- auto faceVert = *faceVertices.begin();
+ // Invert the texture matrix so we can transform vertices from 2D texture space back to 3D world space
+ if (!textureTrsf.Invert())
+ {
+ printf("Failed to invert texture space transform!\n");
+ return polygons;
+ }
// Create a virtual grid at the texture bounds and iterate over each cell to break up the face into repeating tiles
for (double y = floor(minT); y <= ceil(maxT); y += 1.0)
@@ -85,10 +92,9 @@ std::vector Tesselator::tesselateFace(const face_t* face)
for (int v = 0; v < result.contour[0].num_vertices; ++v)
{
const auto vert = &result.contour[0].vertex[v];
- Vec3 newVert =
- plane->normal * plane->dist +
- texinfo->vectorS * (vert->x * miptex->width - texinfo->distS) * invSLenSqr +
- texinfo->vectorT * (vert->y * miptex->height - texinfo->distT) * invTLenSqr;
+
+ // Transform the vertex back to world space
+ Vec3 newVert = textureTrsf.TransformPoint(Vec3(vert->x, vert->y, 0));
size_t vertIndex = addVertex(newVert);
Vec3 normalizedUV(vert->x - x, vert->y - y, 0); // Normalize the UV to fall within [0..1] range
@@ -103,12 +109,6 @@ std::vector Tesselator::tesselateFace(const face_t* face)
}
}
- if (vertexIndices.find(faceVert) == vertexIndices.end())
- {
- gpc_free_polygon(&facePolygon);
- return polygons;
- }
-
gpc_free_polygon(&facePolygon);
return polygons;
}