N-gon SPMBs in Curvy use very simple approach to UV mapping in their Absolute mode (it's hard to see any use for Stretch V modes we we'll ignore them here):
It's actually quite easy to solve all those issues with a few simple changes to SPMB code.
1. Making planar mapping proportional. It's reasonable to assume that people usually want their texels to be uniform. Even if we're using planar projection, topmost and bottommost areas of the extrusion, where planar distortion is the least apparent, should ideally have neat square texels. Well, that should be easy to achieve: with planar mapping from above, all you have to do is take the extrusion radius into account. First, calculate the new multiplier in the Extrude method of SplinePathMeshBuilder (let's call it uvParameterWidth for consistency with tile height multiplier).
And then apply it when UVs are assigned:
Voila, texels on topmost and bottommost areas of extrusion are always proportionally scaled. And by modifying the "Step Width" (it should really be renamed to UV tile height or something to avoid confusion, though) we can now achieve consistent texel density across multiple extrusions of different radius, thanks to U coordinate not being invariably stretched from 0 to 1 across any radius. Just input the same "Step Width" to any number of splines, and no matter their radius, they will look consistent with no need for additional input.
2. Ditching planar mapping. Ideally, though, there should be an option for wrap-around mapping where U coordinate is determined by the loop vertex index divided by the loop vertex count. There are two ways to implement it. To avoid removing planar mapping (maybe it's useful to someone) I simply added another entry to the MeshUV enum called AbsoluteWrapped. Then I check for it in the aforementioned Extrude method and do this:
Of course, we still want the texels to be uniform, so we have to find a way to scale U (vcoord) proportionately. Obviously, that can be done by calculating circumference of the extrusion. Assuming our N-gon is closer to a circle, it's 6.28f * CapWidth. Just as vcoord, there is no point in calculating circumeference at each ev index, so do that outside of ev for loop, right under vcoord, and then apply it:
And voila, we no longer have awful stretching on the sides:
There is only one problem, though: Curvy is not generating two vertices at the edge loop end, which makes it impossible to use a UV seam and forces a texture to wrap backward to 0 on the very last face:
I'm not familiar enough with the Curvy code to jam an additional vertex into the loops - I don't know how to do it and I'm fairly sure you are using the size of the vertex array as a stand-in for expected side count, so that change might break something. So, that would be my feature request - add a duplicate last vertex to the end of N-gon loops (and rectangles, I guess). That's how all pipes and wires in the world are usually textured, they have a duplicate edge running across all length, allowing you to start and end your UV island at any U coordinate without any inconvenient stretching.
And while that's not in place, there is a workaround. Instead of using whole tile horizontally over the circumference, use half the tile and start traversing U backwards as you cross half the indexes in a loop:
That will give you two mirror seams on the topmost and bottommost edges of the extrusion, but it will look tolerable:
_____________________________
Feel free to use those changes anywhere and pretty please, change the implementation of extrusion loops by adding an aforementioned additional vertex that can enable UV seams.
P.S.: Here is the UV grid texture if anyone needs it: http://i.imgur.com/mMXpWQj.png
- U coordinate of every vertex is simply StartMeshInfo.EdgeUV[E.vertexIndex[ev]].x, which basically gives you planar mapping projected from above
- V coordinates (UV tile height) depend on arbitrary world space length from UVParameter (dubbed "Step width" in the inspector)
- You can't really use maps with detail because detail will never be uniformly applied across extruded N-gon loops (being projected from above)
- Artists have to do non-obvious manual adjustment of "Step width" (UV tile height) parameter for every given N-gon radius if they want to avoid stretching of texels
- You can't reuse textures if you want to get uniform texel densities across multiple extrusions of different radius, because any radius always receives whole 0-1 U space
It's actually quite easy to solve all those issues with a few simple changes to SPMB code.
1. Making planar mapping proportional. It's reasonable to assume that people usually want their texels to be uniform. Even if we're using planar projection, topmost and bottommost areas of the extrusion, where planar distortion is the least apparent, should ideally have neat square texels. Well, that should be easy to achieve: with planar mapping from above, all you have to do is take the extrusion radius into account. First, calculate the new multiplier in the Extrude method of SplinePathMeshBuilder (let's call it uvParameterWidth for consistency with tile height multiplier).
Code:
float uvParameterWidth = (CapWidth * 2f) / UVParameter;
And then apply it when UVs are assigned:
Code:
mUV[vIndex + ev] = new Vector2(StartMeshInfo.EdgeUV[E.vertexIndex[ev]].x * UVParameterWidth, vcoord);
Voila, texels on topmost and bottommost areas of extrusion are always proportionally scaled. And by modifying the "Step Width" (it should really be renamed to UV tile height or something to avoid confusion, though) we can now achieve consistent texel density across multiple extrusions of different radius, thanks to U coordinate not being invariably stretched from 0 to 1 across any radius. Just input the same "Step Width" to any number of splines, and no matter their radius, they will look consistent with no need for additional input.
2. Ditching planar mapping. Ideally, though, there should be an option for wrap-around mapping where U coordinate is determined by the loop vertex index divided by the loop vertex count. There are two ways to implement it. To avoid removing planar mapping (maybe it's useful to someone) I simply added another entry to the MeshUV enum called AbsoluteWrapped. Then I check for it in the aforementioned Extrude method and do this:
Code:
mUV[vIndex + ev] = new Vector2 (((float) ev / (float) E.vertexCount), vcoord);
Of course, we still want the texels to be uniform, so we have to find a way to scale U (vcoord) proportionately. Obviously, that can be done by calculating circumference of the extrusion. Assuming our N-gon is closer to a circle, it's 6.28f * CapWidth. Just as vcoord, there is no point in calculating circumeference at each ev index, so do that outside of ev for loop, right under vcoord, and then apply it:
Code:
mUV[vIndex + ev] = new Vector2 (((float) ev / (float) E.vertexCount), vcoord / circumference);
And voila, we no longer have awful stretching on the sides:
There is only one problem, though: Curvy is not generating two vertices at the edge loop end, which makes it impossible to use a UV seam and forces a texture to wrap backward to 0 on the very last face:
I'm not familiar enough with the Curvy code to jam an additional vertex into the loops - I don't know how to do it and I'm fairly sure you are using the size of the vertex array as a stand-in for expected side count, so that change might break something. So, that would be my feature request - add a duplicate last vertex to the end of N-gon loops (and rectangles, I guess). That's how all pipes and wires in the world are usually textured, they have a duplicate edge running across all length, allowing you to start and end your UV island at any U coordinate without any inconvenient stretching.
And while that's not in place, there is a workaround. Instead of using whole tile horizontally over the circumference, use half the tile and start traversing U backwards as you cross half the indexes in a loop:
Code:
if ((float) ev < (float) E.vertexCount / 2f)
mUV[vIndex + ev] = new Vector2 (((float) ev / (float) E.vertexCount), vcoord / circumference);
else
mUV[vIndex + ev] = new Vector2 (((float)(E.vertexCount - ev) / (float) E.vertexCount), vcoord / circumference);
That will give you two mirror seams on the topmost and bottommost edges of the extrusion, but it will look tolerable:
_____________________________
Feel free to use those changes anywhere and pretty please, change the implementation of extrusion loops by adding an aforementioned additional vertex that can enable UV seams.
P.S.: Here is the UV grid texture if anyone needs it: http://i.imgur.com/mMXpWQj.png