CGTalk > Software > Autodesk 3ds max > 3dsMax SDK and MaxScript
Login register
Thread Closed share thread « Previous Thread | Next Thread »  
 
Thread Tools Search this Thread Display Modes
Old 12-11-2010, 03:19 PM   #1
plastic
Down With The Industry
 
plastic's Avatar
portfolio
Marc Lorenz
Vienna, Austria
 
Join Date: Dec 2002
Posts: 789
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...


Code:
( 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 ) )
 
Old 12-11-2010, 03:59 PM   #2
PiXeL_MoNKeY
Expert
 
PiXeL_MoNKeY's Avatar
Eric Craft
Dallas, USA
 
Join Date: Mar 2002
Posts: 5,662
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."
 
Old 12-12-2010, 09:38 PM   #3
Caprier
Frequenter
 
Caprier's Avatar
Pat Garret
Southern Alps, France
 
Join Date: Jan 2008
Posts: 113
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
 
Old 12-14-2010, 10:02 AM   #4
plastic
Down With The Industry
 
plastic's Avatar
portfolio
Marc Lorenz
Vienna, Austria
 
Join Date: Dec 2002
Posts: 789
Quote:
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)
 
Old 12-14-2010, 11:36 AM   #5
Caprier
Frequenter
 
Caprier's Avatar
Pat Garret
Southern Alps, France
 
Join Date: Jan 2008
Posts: 113
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-14-2010 at 11:43 AM.
 
Old 12-14-2010, 09:04 PM   #6
Klunk
Lord of the posts
 
Klunk's Avatar
portfolio
Klunk
United Kingdom
 
Join Date: Sep 2005
Posts: 701
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
Code:
local hitFaces=for f in theFaceVertsUVCoords collect PointInTriangle f[1] f[2] f[3] UVCoord --Check if point is in triangle

to
Code:
local hitFaces=for f in theFaceVertsUVCoords collect PointInTriangle2 f[1] f[2] f[3] UVCoord --Check if point is in triangle

change
Code:
appendIfUnique vertexPositions (v1*baryCoords.x + v2*baryCoords.y + v3*baryCoords.z)

to
Code:
appendIfUnique vertexPositions (v1 * hitFaces[f].x + v2 * hitFaces[f].y + v3 * (1 - (hitFaces[f].y + hitFaces[f].x)))

you can delete
Code:
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-14-2010 at 09:09 PM.
 
Old 12-14-2010, 09:44 PM   #7
Klunk
Lord of the posts
 
Klunk's Avatar
portfolio
Klunk
United Kingdom
 
Join Date: Sep 2005
Posts: 701
sorry that should read

Code:
appendIfUnique vertexPositions (v1 * (1 - (hitFaces[f].y + hitFaces[f].x)) + v2 * hitFaces[f].y + v3 * hitFaces[f].x)
 
Old 12-15-2010, 12:01 PM   #8
plastic
Down With The Industry
 
plastic's Avatar
portfolio
Marc Lorenz
Vienna, Austria
 
Join Date: Dec 2002
Posts: 789
Quote:
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.
 
Old 12-15-2010, 12:25 PM   #9
plastic
Down With The Industry
 
plastic's Avatar
portfolio
Marc Lorenz
Vienna, Austria
 
Join Date: Dec 2002
Posts: 789
Quote:
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:
Code:
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:
Code:
( 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 ) )
 
Old 12-15-2010, 12:25 PM   #10
CGTalk Moderation
Lord of the posts
CGTalk Forum Leader
 
Join Date: Sep 2003
Posts: 1,066,480
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.
 
Thread Closed share thread


Thread Tools Search this Thread
Search this Thread:

Advanced Search
Display Modes

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 vBulletin
Copyright ©2000 - 2006,
Jelsoft Enterprises Ltd.
Minimize Ads
Forum Jump
Miscellaneous

All times are GMT. The time now is 05:26 PM.


Powered by vBulletin
Copyright ©2000 - 2016, Jelsoft Enterprises Ltd.