Generating Texture Coordinates for a Flat Mesh

Started by
5 comments, last by ShadowyCore 14 years, 6 months ago
Hey everybody! The problem I'm faced with is in the generation of texture coordinates for a flat set of points, made up of a convex polygon. This is drawn as a triangle fan, but I'm having some issues assignging texture coordinates. So far, the planes exist with normals only in the cardinal directions, but those with normals along the X axis seem to have weirdly contorted coordinates. The algorithm I've been using seems to create NaN and Infinity for these coordinates. Here's the code (this is C# with XNA, by the way): private static Vector2 getTexCoords(ref Vector3 source, ref List<Vector3> points) { Vector3 max = points[0]; Vector3 min = points[0]; // Get Minimum for (int i = 0; i < points.Count; i++) { Vector3 point = points; Vector3.Max(ref max, ref point, out max); Vector3.Min(ref min, ref point, out min); } // Lines Vector3 topLeft, topRight = max, bottomLeft = min, bottomRight; topLeft = new Vector3(bottomLeft.X, topRight.Y, topRight.Z); bottomRight = new Vector3(topRight.X, bottomLeft.Y, bottomLeft.Z); // Get distance from lines Vector3 topClosest = ClosestPointOnLineSegment(ref topLeft, ref topRight, ref source); Vector3 leftClosest = ClosestPointOnLineSegment(ref topLeft, ref bottomLeft, ref source); float topDist = Vector3.Distance(source, topClosest); float leftDist = Vector3.Distance(source, leftClosest); // Generate texture coordinates Vector2 ret = new Vector2(); //ret.X = MathHelper.Lerp(0f, 1f, Vector3.Distance(topLeft, topRight) / leftDist); //ret.Y = MathHelper.Lerp(1f, 0f, Vector3.Distance(topLeft, bottomLeft) / topDist); ret.X = (MathHelper.Lerp(0f, 1f, leftDist / Vector3.Distance(topLeft, topRight))); ret.Y = (MathHelper.Lerp(1f, 0f, topDist / Vector3.Distance(topLeft, bottomLeft))); ret.X = infnanchk(ret.X); ret.Y = infnanchk(ret.Y); return ret; } public static Vector3 ClosestPointOnLineSegment(ref Vector3 lineA, ref Vector3 lineB, ref Vector3 sourcePoint) { Vector3 v = lineB - lineA; if (v == Vector3.Zero) return (lineA + lineB) / 2f; v.Normalize(); float t = Vector3.Dot(v, sourcePoint - lineA); if (t < 0) return lineA; float d = (lineB - lineA).Length(); if (t > d) return lineB; return lineA + v * t; }
Advertisement
Quote:Original post by ShadowyCore
The algorithm I've been using seems to create NaN and Infinity for these coordinates.


Work backwards. The most obvious explanation is probably a division by zero, put in some code to work out which one it is.
The Trouble With Robots - www.digitalchestnut.com/trouble
It is a divide by zero problem. In the Y parameter of my texture coords, I use the distance between the top left and bottom left corners. For some reason, these become the same. Distance is zero, divide by zero = Infinity.

So, I guess my real question is if there is a better way to do what I'm trying to do. Is there a better way to create these coordinates? Is there something wrong with my top-left, bottom-right generation code?
I believe your problem is this section:

// LinesVector3 topLeft, topRight = max, bottomLeft = min, bottomRight;topLeft = new Vector3(bottomLeft.X, topRight.Y, topRight.Z);bottomRight = new Vector3(topRight.X, bottomLeft.Y, bottomLeft.Z);


I don't think these points have the properties you think they do, not for all orientations anyway. What you're after is four points in the plane of the polygon that form a bounding rectangle for that polygon. The way I would do this involves 'dot products' and 'cross products', which are standard tools for solving this kind of problem. Are you familiar with these?
The Trouble With Robots - www.digitalchestnut.com/trouble
I'm familiar with them, I just hadn't considered them for what I was going to do. The problem is finding a way to generate correct coordinates for these points, no matter the orientation of the plane.

. A _ _ _ _ _ Max (B)
. .|
. .|
. .|
. .|_ _ _ _ _
Min (C) . . . . D

Finding A and D seems to be the problem. B and C are just Vector3.Min and Vector3.Max checks, so that doesn't seem to be a problem. I can't just run checks to find the vertices for these corners, either, because the polygon could have any number of sides.

Perhaps I could use the dot product and distance between the two known points to find the direction and distance to the two other points. Some trigonometry could come in handy here.
Quote:Original post by ShadowyCore
B and C are just Vector3.Min and Vector3.Max checks, so that doesn't seem to be a problem.


I don't think that's always true. Consider the polygon:
 (0, 0, 0) (1, -1, 0) (1, -1, 1) (0, 0, 1)


In this case Vector3.Max will be (1, 0, 1), which isn't on the plane of the polygon (x + y = 0).

I would suggest:

1. Find two normalized perpendicular vectors on the plane of the polygon. These could be:
 v1 = normalize(points[1] - points[0]) // some vector on the polygon v2 = cross(v1, polygon.normal)        // perpendicular to v1 on the polygon  where polygon.normal = cross(v1, normalize(points[2] - points[0]))   (assuming points 0, 1, 2 aren't collinear)


2. Find co-ordinates of each point on the polygon in terms of those directions:
 u = dot_product(point, v1) v = dot_product(point, v2)


3. Find the extents of the u values, say min_u and max_u, and similarly for the v values.

4. At this point you could find your four points by transforming co-ordinates (min_u, min_v), (max_u, min_v), (max_u, max_v) and (min_u, max_v) back into 3D space, something like:
 top_left = (min_u * v1) + (min_v * v2) + dot_product(point[0], normal) ...


5. Alternatively to (4), you already have texture co-ordinates for each point, you can just remap each of them into the range [0 .. 1]:
 u := (u - min_u) / (max_u - min_u) v := (v - min_u) / (max_u - min_u)


I hope I haven't made any mistakes!
The Trouble With Robots - www.digitalchestnut.com/trouble
I seem to have arrived at a solution. I used this instead of the min/max picking I used before. Geoffrey, I realized that the corners were not necessarily (and won't usually) be on the polygon earlier, so that was spot on. If my solution breaks down, I'll look more carefully at yours. I had thought of something similar to that a little earlier, but I couldn't seem to find another vector based on my supposed min/max points.

I preferred to think of the points as pushing walls out of their way. It's more like a box, now, than anything else. That meant that I couldn't use Vector3.Max anymore, but that's okay.

//Vector3 max = transformed[0];
//Vector3 min = transformed[0];
float minX = transformed[0].X, minY = transformed[0].Y, minZ = transformed[0].Z;
float maxX = transformed[0].X, maxY = transformed[0].Y, maxZ = transformed[0].Z;

// Get Minimum
for (int i = 0; i < transformed.Count; i++)
{
Vector3 point = transformed;

if (point.X < minX) minX = point.X;
else if (point.X > maxX) maxX = point.X;

if (point.Y < minY) minY = point.Y;
else if (point.Y > maxY) maxY = point.Y;

if (point.Z < minZ) minZ = point.Z;
else if (point.Z > maxZ) maxZ = point.Z;

//Vector3.Max(ref max, ref point, out max);
//Vector3.Min(ref min, ref point, out min);
}

Vector3 min = new Vector3(minX, minY, minZ);
Vector3 max = new Vector3(maxX, maxY, maxZ);

// Lines
//Vector3 topLeft, topRight = max, bottomLeft = min, bottomRight;
//topLeft = new Vector3(bottomLeft.X, topRight.Y, topRight.Z);
//bottomRight = new Vector3(topRight.X, bottomLeft.Y, bottomLeft.Z);

Vector3 topLeft; // = new Vector3(minX, maxY, maxZ);
Vector3 topRight; // = new Vector3(maxX, maxY, maxZ);
Vector3 bottomLeft; // = new Vector3(minX, minY, minZ);
Vector3 bottomRight; // = new Vector3(maxX, minY, minZ);

if (min.X == max.X)
{
topLeft = new Vector3(maxX, maxY, minZ);
topRight = new Vector3(maxX, maxY, maxZ);
bottomLeft = new Vector3(minX, minY, minZ);
}
else if (min.Y == max.Y)
{
topLeft = new Vector3(minX, minY, maxZ);
topRight = new Vector3(maxX, maxY, maxZ);
bottomLeft = new Vector3(minX, minY, minZ);
}
else if (min.Z == max.Z)
{
topLeft = new Vector3(minX, maxY, minZ);
topRight = new Vector3(maxX, maxY, maxZ);
bottomLeft = new Vector3(minX, minY, minZ);
}
else
{
topLeft = new Vector3(minX, maxY, maxZ);
topRight = new Vector3(maxX, maxY, maxZ);
bottomLeft = new Vector3(minX, minY, minZ);
}

This topic is closed to new replies.

Advertisement