PDA

View Full Version : UV location to mesh location


Eugenio
07-25-2011, 04:22 PM
I want to create a function that receives a mesh and a 2D coordinate as input and returns a point3 (maybe many) on the mesh where that 2D point lies, based on the UV coordinates of the mesh.

But it's not so easy though, as the input isn't at specific vertex locations, but anywhere in UV space. So it might or might not have any information at a given point. Not only that but it also can return multiple points, in case of overlapping faces in UV space.

In resume I'm looking for a function that looks like this: UV_To_Mesh_Sample theMesh theUV and returns an array of point3.

Regards,
Jr.

PiXeL_MoNKeY
07-25-2011, 04:30 PM
Look up Barycentric Coordinates (http://docs.autodesk.com/3DSMAX/14/ENU/MAXScript%20Help%202012/files/GUID-D1D7EB56-A370-4B07-99B4-BC779FB87CA-739.htm#WS73099CC142F48755-2231E4B3128F27E2F8C-336E) in the Maxscript Help.

-Eric

plastic
07-25-2011, 05:22 PM
Search for threads started by me with "uvw" in the title.
I got some half-working solution. The problem is that it only works with default uvw maps on primitives, not with a uvw map modifier, as far as I remember. The whole uvw thing in max is a nightmare, IMO.

Bobo
07-25-2011, 05:32 PM
Search for threads started by me with "uvw" in the title.
I got some half-working solution. The problem is that it only works with default uvw maps on primitives, not with a uvw map modifier, as far as I remember. The whole uvw thing in max is a nightmare, IMO.

Not really, you can call snapshotAsMesh() to get the TriMesh of any object, the rest is trivial meshOp. mapping calls (there is a whole section in the help about accessing all 100 map channels in a mesh).

denisT
07-25-2011, 05:36 PM
I want to create a function that receives a mesh and a 2D coordinate as input and returns a point3 (maybe many) on the mesh where that 2D point lies, based on the UV coordinates of the mesh.
In resume I'm looking for a function that looks like this: UV_To_Mesh_Sample theMesh theUV and returns an array of point3.


do it step by step...
step #1: can you make a function to check if some 2D point inside some 2D triangle?
we need this function to find all UV faces which contain the UV point.

Eugenio
07-25-2011, 11:04 PM
Thanks a lot for the answer guys. But I'm still stuck in this problem...I did most of what you told me already, but as I didn't want to get into the details of my tries to keep the post as short as possible.

step #1: can you make a function to check if some 2D point inside some 2D triangle?we need this function to find all UV faces which contain the UV point.I already did a function to find the Barycentric Coordinates. However that's not my main problem.

Suppose I have this part up and running. Like say, I pass the coordinates [0.3, 0.2, 0] in UVW space and I find 3 faces containing that point. Fine. But what I really need is where in 3D space the point [0.3, 0.2, 0] actually lies for each face. To my logic seems more like the inverse of the barycentric coordinates, something I don't know how to do.

Another problem is that I'm going to have too many geometry queries, because at every step the script goes in 2D, I have to go through all triangles in the mesh to check if it falls inside a face or faces. Maybe there's an easier way of doing this, but I really can't see other than using acceleration structures and that is far more trouble this script deserves.

Actually if it gets too complicated I have to give up on the idea because there's no time to develop it...

Purpose of the script: This is my reference: http://www.youtube.com/watch?v=rUFnH7KYMFE.
I have to do something similar, and I already got a nice procedural pattern, so I was looking for a method of wrapping it into arbitrary meshes. The best idea that came to mind was generating it in UV space and converting the values to 3D.

Regards,
Jr.

plastic
07-25-2011, 11:11 PM
well here is what I got:

(
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...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
)
)


Result:
Grid of point helpers with 5 subdivs on U and V on a box with default mapping:
http://666kb.com/i/bp57soayfmztv3lrw.png
For a given UV coordinate, there are 6 points in world space, in this case (box mapping).

There is a problem with this code I couldn't figure out.
When the original box is segmented, something strange happens, probably related to a rounding error? when uv point lies on a edge then the corresponding face cant be found...or something... :shrug:

http://666kb.com/i/bp95kmn5io3c5bo58.jpg

http://666kb.com/i/bp95qvnlezylt83cs.jpg

Eugenio
07-27-2011, 01:02 AM
Plastic, thanks a ton man! Although I was almost done, you still helped me. I finally managed to resolve my main issues! :bounce:
My code seems to be working nicely, but it's still wip. I didn't care about UV Tiling for now as I'm probably not going to need it. At least it respects correctly the mapping coordinates.

There are problems of course, but are not so apparent in the code below because I'm just creating point helpers at sample points, but as for my needs I must create a spline out of those, so they must be created in proper order otherwise the spline will look like a mess.

Now I need to think in a way to optimize it as as I said it goes through all the mesh for every sample point. Any suggestion is again very welcome... :thumbsup:

And I'm doing a really nasty trick converting a float to string and the string back to float to get around a maxscript inaccuracy comparing a variable to 1.0. I'm not sure the best (and faster) solution for that.

To try out the code just copy it and create a new script and with some object selected run it.
NOTE: Coordinates with tiling (values out of 0-1 range) will not be taken into account.

(
obj = snapShotAsMesh $
theNumFaces = meshop.getNumFaces obj
theWorldPos = #()
step = 10.0

--Find Barycentric Coordinates Function
fn find_barycoords_fn obj theFace theCoord =
(
--Gets the UVW vertices of the given face
theFaceVerts = getTvFace obj theFace

--Gets the UVW position of each face vertex.
--NOTE: No value is needed for the W coordinate, so the multiplication cancels it out.
v1 = getTVert obj theFaceVerts[1] * [1, 1, 0]
v2 = getTVert obj theFaceVerts[2] * [1, 1, 0]
v3 = getTVert obj theFaceVerts[3] * [1, 1, 0]

--Calculates the edges of the internal triangles
edge1 = theCoord - v1
edge2 = theCoord - v2
edge3 = theCoord - v3

--Calculates the total area of the parallelogram formed by the triangle
ta = length (cross (v1-v2) (v3-v2))

a1 = cross edge1 edge3
a2 = cross edge2 edge3
a3 = cross edge2 edge1

bc1 = length (a1/ta)
bc2 = length (a2/ta)
bc3 = length (a3/ta)

theTotal = (bc1 + bc2 + bc3) as string

--If the sum of the three components of the barycentric coordinates is bigger than one it means the point is outside the triangle
if theTotal as float <= 1.0 then
bc = [bc1, bc2, bc3]
else
bc = undefined

)

for v=0 to 1 by 1/step do
for u=0 to 1 by 1/step do
for f=1 to theNumFaces do
(
--Returns the barycentric coordinates in UV space for a given UVW point
bc = find_barycoords_fn obj f [u, v, 0]

--Gives a Point3 with 3 vertex indices
theFace = getFace obj f

v1=getVert obj theFace[2]
v2=getVert obj theFace[1]
v3=getVert obj theFace[3]

if bc != undefined then
appendIfUnique theWorldPos (v1 * bc.x + v2 * bc.y + v3 * (1 - (bc.y + bc.x)))

)

pt = point()
for i in theWorldPos do
(
newPt = instance pt
newPt.pos = i
newPt.wireColor = green
)
delete pt
)

plastic
07-27-2011, 03:35 AM
Hey Eugenio,
I'm looking forward trying your code but I'm on vacation right now and cant test it with my laptop. Will give it a run in a few days.
For the comparing float issue, you may try using the closeEnough() function, instead of a "==" compare.
Also try converting your numerical values to "Double" precision instead of the default "Float".

CGTalk Moderation
07-27-2011, 03:35 AM
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.