# I don't understand the "W" in "UVW"

 12 December 2010 plastic Down With The Industry   portfolio Marc Lorenz Vienna, Austria I don't understand the "W" in "UVW" I'm working on a way to use the UV mapping information for regularly distributing objects on a poly surface, independently of the underlying triangulation/segments. I'm having success, as long as I use the original mapping information, generated by 3dsmax, when creating the objects. But as soon as I use the "UVW Map" modifier, things no longer work, because the mapping is different. For example, with a plane primitive, the UV map is a planar projection going from U=0..1 and V=0..1. W is always zero. (As I expect it, with planar projection). When I put a new planar UVW map on top, then it's no longer as expected, but there are also W coordinates involved. I'm converting the UV coordinates to barycentric coordinates, so I have no clue what to do with those W coordinates, and why they are here in the first place. When using the mapping for a bitmap, both versions look the same of course. What I'm trying to achieve is nothing different than projecting a bitmap. (Grid of pixels vs grid of world coordinates) There are many old threads where people looked for that functionality and gave up. I'm happy that I got it working to some degree, but it would be cool if it also worked with the "UVW Map" modifier. I'd be happy for any input. Box with standard mapping: Box with standard mapping, slightly rotated by UVW Xform modifier...result as expected: UVW Map modifier on top with box projection. Should be the same as the first pic? But it's not what I'm expecting and I don't understand what's going on here... `````` ( delete \$Point* local meshObj = snapShotAsMesh \$ local theChannel=1 local stepsU=5 as Double local stepsV=5 as Double local totalSteps=stepsU*stepsV --Point in triangle test function SameSide p1 p2 a b = dot (cross (b-a) (p1-a)) (cross (b-a) (p2-a))>=0 function PointInTriangle a b c p = (SameSide p a b c) and (SameSide p b a c) and (SameSide p c a b) --Alternative Point in triangle test...returns returns point2 value, or false if point outside. --http://www.blackpawn.com/texts/pointinpoly/default.html fn pointInTriangle2 a b c thisPoint = ( local v1 = c-a local v2 = b-a thisPoint.z=0 --only interested in the 2d plane local v3 = thisPoint-a local dot11 = dot v1 v1 local dot12 = dot v1 v2 local dot13 = dot v1 v3 local dot22 = dot v2 v2 local dot23 = dot v2 v3 local div = 1 / (dot11 * dot22 - dot12 * dot12) local u = (dot22 * dot13 - dot12 * dot23) * div local v = (dot11 * dot23 - dot12 * dot13) * div if (u > 0) and (v > 0) and (u + v <= 1) then [u,v] else false ) --fast version, only valid if point really in triangle? fn buildBarycentricCoordinates a b c p = ( local v1 = p-a local v2 = p-b local v3 = p-c local a1= (length(cross v2 v3))/2 local a2= (length(cross v3 v1))/2 local a3= (length(cross v1 v2))/2 local ta= a1+a2+a3 [a1/ta, a2/ta, a3/ta] ) --Bobo´s long version fn buildBarycentricCoordinates2 v1 v2 v3 p= ( local vector1 = p - v1 --this is the vector from vertex 1 to the dummy local vector2 = p - v2 --this is the vector from vertex 2 to the dummy local vector3 = p - v3 --this is the vector from vertex 3 to the dummy --Calculate the cross product of the 3 vectors local theCross1 = (cross vector2 vector3) local theCross2 = (cross vector3 vector1) local theCross3 = (cross vector1 vector2) --calculate the face normal using the cross product of two edges local theNormal = normalize (cross (v2-v1) (v3-v1)) --because the length of the cross product vector is equal to the area of the parallelogram --defined by the two operands, half of it is the area of the triangle! local area1 = (length theCross1 )/2 --this is the area of the first sub-triangle local area2 = (length theCross2 )/2 --this is the area of the second sub-triangle local area3 = (length theCross3 )/2 --this is the area of the third sub-triangle --calculate the angle of each cross product with the face normal local angle1 = acos ( dot ( normalize theCross1) theNormal ) local angle2 = acos ( dot ( normalize theCross2) theNormal ) local angle3 = acos ( dot ( normalize theCross3) theNormal ) --if the angle is different from the other two, take its area as negative if angle1 != angle2 and angle1 != angle3 then area1 = -area1 if angle2 != angle1 and angle2 != angle3 then area2 = -area2 if angle3 != angle1 and angle3 != angle2 then area3 = -area3 local fullArea = area1 + area2 + area3 --this is the full area of the triangle local b1 = area1 / fullArea --this is the proportion of the first triangle vs. the full triangle local b2 = area2 / fullArea --this is the proportion of the second triangle vs. the full triangle local b3 = area3 / fullArea --this is the proportion of the third triangle vs. the full triangle [b1, b2, b3] --Behold! Hand-made Barycentric coordinates!!!l ) --build array with steps grid fn buildStepsArray stepsU stepsV= ( local counteri1=0 local counteri2=0 local UVSearch=#() for i1=0 to 1 by (1/stepsU) do ( counteri1+=1 for i2=0 to 1 by (1/stepsV) do ( counteri2+=1 local result=[i1,i2,0] append UVSearch result ) ) UVSearch ) UVSearch=buildStepsArray stepsU stepsV --collect all faces local meshFaces=meshObj.faces as bitarray --collect all map verts for each face local theMapFaces=for f in meshFaces collect meshop.getMapFace meshObj theChannel f --collect UV coords of each face/vertex local theFaceVertsUVCoords=for v in theMapFaces collect #(meshop.getMapVert meshObj theChannel v.x, meshop.getMapVert meshObj theChannel v.y, meshop.getMapVert meshObj theChannel v.z) vertexPositions=#() for UVCoord in UVSearch do ( --collect all affected faces local hitFaces=for f in theFaceVertsUVCoords collect PointInTriangle f[1] f[2] f[3] UVCoord --Check if point is in triangle for f=1 to hitFaces.count where hitFaces[f]!=false do ( a=theFaceVertsUVCoords[f][1] b=theFaceVertsUVCoords[f][2] c=theFaceVertsUVCoords[f][3] baryCoords = buildBarycentricCoordinates a b c UVCoord theFace = getFace meshObj f --gives a Point3 with 3 vertex indices v1=getVert meshObj theFace.x v2=getVert meshObj theFace.y v3=getVert meshObj theFace.z appendIfUnique vertexPositions (v1*baryCoords.x + v2*baryCoords.y + v3*baryCoords.z) ) ) for i in vertexPositions do ( point pos:i ) )`````` __________________ www.marclorenz.com www.facebook.com/marclorenzvisualization share quote
 12 December 2010 PiXeL_MoNKeY Expert   Eric Craft Dallas, USA You can do cubic/UVW mapping with the planar. I do it all the time manually by switching to box, setting the height value and switching back. So you should be able to get/set the height parameter of the planar map. For best results I would use XYZ to UVW mapping as it will map the world coordinates to UVW for you. -Eric __________________ "The Evil Monkey hiding in your closet." share quote
 12 December 2010 Caprier Frequenter   Pat Garret Southern Alps, France Quote: For example, with a plane primitive, the UV map is a planar projection going from U=0..1 and V=0..1. W is always zero. (As I expect it, with planar projection). W is zero not because of planar mapping but because the mapped object is a plane. If you apply a planar mapping to an object you also get W values according to the object's vertical dimensions. It's like rescaling the object so that its bounding box fits in a 1x1x1 units cube. The Edit UVWs dialog is kind of a viewport with three orthographic views: UV <=> top VW <=> right UW <=> front share quote
 12 December 2010 plastic Down With The Industry   portfolio Marc Lorenz Vienna, Austria Originally Posted by Caprier: W is zero not because of planar mapping but because the mapped object is a plane. If you apply a planar mapping to an object you also get W values according to the object's vertical dimensions. It's like rescaling the object so that its bounding box fits in a 1x1x1 units cube. The Edit UVWs dialog is kind of a viewport with three orthographic views: UV <=> top VW <=> right UW <=> front OK I understand that. Where I'm stuck is this: Each (bitmap) texture is 2d/XY/UV. As long as the mapping goes UV without W (W=0), then it's no problem to convert the map coordinates to surface UV coordinates using baycentric calculations, point in triangle, etc. (as with my code above). But when there is a W value I have no clue how 2d mapping works in this case. Bitmaps get projected, so there must be some method to project bitmap XY to UVW, but how? With my code above I just ignored W (always 0), but it's obviously wrong. (3rd image) __________________ www.marclorenz.com www.facebook.com/marclorenzvisualization share quote
 12 December 2010 Caprier Frequenter   Pat Garret Southern Alps, France If I understand correctly, in your 3rd image you're using box mapping. In this case it's like a planar mapping applied from the three directions only to the polys facing that direction. For the top and bottom polys, you get U <=> X and V <=>Y. For the ones perpendicular to the X axis, it's U <=>Y and V <=> Z. And for the last ones, U <=> X and V <=> Z. (With one of the two sides flipped) If what you're trying to do is go from UVW space to XYZ, you might want to try the unwrap interface. It has a very useful method called getVertexGeomIndexFromFace() that returns the vertex indices of a geometric face from the corresponding texture face. (It can get very messy if working on a polymesh instead of a trimesh though, depending on the number of verts per poly) From there, computing a position on a texture face from the UVs and finding the corresponding position on the object face is relatively easy. But you need to add an unwrap modifier. Last edited by Caprier : 12 December 2010 at 11:43 AM. share quote
 12 December 2010 Klunk Lord of the posts   portfolio Klunk United Kingdom I don't know why I'm doing this because you didn't read/listen to what I posted last time. It has nothing to do with the UVW , W is always assumed to be zero so has no effect on the outcome change ``local hitFaces=for f in theFaceVertsUVCoords collect PointInTriangle f[1] f[2] f[3] UVCoord --Check if point is in triangle`` to ``local hitFaces=for f in theFaceVertsUVCoords collect PointInTriangle2 f[1] f[2] f[3] UVCoord --Check if point is in triangle`` change ``appendIfUnique vertexPositions (v1*baryCoords.x + v2*baryCoords.y + v3*baryCoords.z)`` to ``````appendIfUnique vertexPositions (v1 * hitFaces[f].x + v2 * hitFaces[f].y + v3 * (1 - (hitFaces[f].y + hitFaces[f].x))) `````` you can delete ``````a=theFaceVertsUVCoords[f][1] b=theFaceVertsUVCoords[f][2] c=theFaceVertsUVCoords[f][3] baryCoords = buildBarycentricCoordinates a b c UVCoord`````` altogether. FYI the u,v in the pointintriangle2 routine are the barycentric coords as you only have to compute 2 (1 - (u +v)) gives you the third. Last edited by Klunk : 12 December 2010 at 09:09 PM. share quote
 12 December 2010 Klunk Lord of the posts   portfolio Klunk United Kingdom sorry that should read ``appendIfUnique vertexPositions (v1 * (1 - (hitFaces[f].y + hitFaces[f].x)) + v2 * hitFaces[f].y + v3 * hitFaces[f].x)`` share quote
 12 December 2010 plastic Down With The Industry   portfolio Marc Lorenz Vienna, Austria Originally Posted by Caprier: If I understand correctly, in your 3rd image you're using box mapping. In this case it's like a planar mapping applied from the three directions only to the polys facing that direction. For the top and bottom polys, you get U <=> X and V <=>Y. For the ones perpendicular to the X axis, it's U <=>Y and V <=> Z. And for the last ones, U <=> X and V <=> Z. (With one of the two sides flipped) If what you're trying to do is go from UVW space to XYZ, you might want to try the unwrap interface. It has a very useful method called getVertexGeomIndexFromFace() that returns the vertex indices of a geometric face from the corresponding texture face. (It can get very messy if working on a polymesh instead of a trimesh though, depending on the number of verts per poly) From there, computing a position on a texture face from the UVs and finding the corresponding position on the object face is relatively easy. But you need to add an unwrap modifier. Hi Caprier, Thanks, I'm going to experiment with getVertexGeomIndexFromFace() I'm pretty close to achieve what I want (UV to XYZ worldcoords) without using the UnwrapUVW modifier though. __________________ www.marclorenz.com www.facebook.com/marclorenzvisualization share quote
 12 December 2010 plastic Down With The Industry   portfolio Marc Lorenz Vienna, Austria Originally Posted by claude666: I don't know why I'm doing this because you didn't read/listen to what I posted last time. It has nothing to do with the UVW , W is always assumed to be zero so has no effect on the outcome Hi Claude, Believe me, I did read what you posted. The problem is, I do not always understand everything. I'm an architect, not a programmer. What I'm doing is messing around with different methods and functions until it works. When I'm done I only understand half of what's going on, even if I wrote everything. Sad but true. Anyway, thanks for your corrections! I'm getting the same result now with original mapping and UVW modifier. There is one issue left though, I didn't have with my old code: I think the problem is in this line: ``if (u >= 0) and (v >= 0) and (u + v <= 1) then [u,v] else false`` Maybe a precision problem? I'm not sure... New version, almost working: ``````( delete \$Point* local meshObj = snapShotAsMesh \$ local theChannel=1 local stepsU=10 as Double local stepsV=10 as Double local totalSteps=stepsU*stepsV --Point in triangle test...returns returns point2 value, or false if point outside. --http://www.blackpawn.com/texts/pointinpoly/default.html fn pointInTriangle a b c thisPoint = ( local v1 = c-a local v2 = b-a thisPoint.z=0 --only interested in the 2d plane local v3 = thisPoint-a local dot11 = dot v1 v1 local dot12 = dot v1 v2 local dot13 = dot v1 v3 local dot22 = dot v2 v2 local dot23 = dot v2 v3 local div = 1 / (dot11 * dot22 - dot12 * dot12) local u = (dot22 * dot13 - dot12 * dot23) * div local v = (dot11 * dot23 - dot12 * dot13) * div if (u >= 0) and (v >= 0) and (u + v <= 1) then [u,v] else ( --format "U:% V:%\n" u v --[u,v] false ) ) --build array with steps grid fn buildStepsArray stepsU stepsV= ( local counteri1=0 local counteri2=0 local UVSearch=#() for i1=0 to 1 by (1/stepsU) do ( counteri1+=1 for i2=0 to 1 by (1/stepsV) do ( counteri2+=1 local result=[i1,i2,0] append UVSearch result ) ) UVSearch ) UVSearch=buildStepsArray stepsU stepsV --collect all faces local meshFaces=meshObj.faces as bitarray --collect all map verts for each face local theMapFaces=for f in meshFaces collect meshop.getMapFace meshObj theChannel f --collect UV coords of each face/vertex local theFaceVertsUVCoords=for v in theMapFaces collect #(meshop.getMapVert meshObj theChannel v.x, meshop.getMapVert meshObj theChannel v.y, meshop.getMapVert meshObj theChannel v.z) vertexPositions=#() for UVCoord in UVSearch do ( --collect all affected faces local hitFaces=for f in theFaceVertsUVCoords collect PointInTriangle f[1] f[2] f[3] UVCoord --Check if point is in triangle for f=1 to hitFaces.count where hitFaces[f]!=false do ( theFace = getFace meshObj f --gives a Point3 with 3 vertex indices v1=getVert meshObj theFace.x v2=getVert meshObj theFace.y v3=getVert meshObj theFace.z appendIfUnique vertexPositions (v1 * hitFaces[f].x + v2 * hitFaces[f].y + v3 * (1 - (hitFaces[f].y + hitFaces[f].x))) ) ) with redraw off for i in vertexPositions do ( point pos:i ) )`````` __________________ www.marclorenz.com www.facebook.com/marclorenzvisualization share quote
 12 December 2010 CGTalk Moderation Expert Thread automatically closed This thread has been automatically closed as it remained inactive for 12 months. If you wish to continue the discussion, please create a new thread in the appropriate forum. __________________ CGTalk Policy/Legalities Note that as CGTalk Members, you agree to the terms and conditions of using this website. share quote

 Posting Rules You may not post new threads You may not post replies You may not post attachments You may not edit your posts vB code is On Smilies are On [IMG] code is On HTML code is Off CGSociety Society of Digital Artists www.cgsociety.org Powered by vBulletinCopyright ©2000 - 2006, Jelsoft Enterprises Ltd.