plastic

12-11-2010, 02:19 PM

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:

http://666kb.com/i/bp57soayfmztv3lrw.png

Box with standard mapping, slightly rotated by UVW Xform modifier...result as expected:

http://666kb.com/i/bp57t61e58eqvmwak.png

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

http://666kb.com/i/bp57tfs73h641olj0.png

(

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

)

)

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:

http://666kb.com/i/bp57soayfmztv3lrw.png

Box with standard mapping, slightly rotated by UVW Xform modifier...result as expected:

http://666kb.com/i/bp57t61e58eqvmwak.png

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

http://666kb.com/i/bp57tfs73h641olj0.png

(

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

)

)