Can I integrate this in new version of MarvelousTool, of course with your permition?
I think this will be very cool addition for MD users.
I recently started to write similar thing using polyOp methods but I was busy this weekend and did not have time to finish the code. You did better approch for sure (a universal solution) which beats all similar tools that I saw.
OK, I’ve been playing with it a litle and here is an small update with a few bugs fixed.
I have changed the Scale word to Size, as it might be easier to understand. So the Size parameter would be the size of the UVW Mesh (width and height).
While it is also the scale, as the UVW range is 0.0-1.0, it might be confusing to figure out what Scale means in that context, as they are two different measuring systems (Geometry units vs. UV units).
Perhaps other people understand it easily, it is just confusing to me.
(
/*
Jorge Rodríguez
jorge@polytools3d.com
Version 0.1 | 08.18.12
Version 0.2 | 02.08.14
+ Added Fix Elements Option ( http://forums.cgsociety.org/showthread.php?f=98&t=1153097)
Version 0.3 | 02.11.14
+ Added Real Scale Option
Version 0.4 | 02.11.14
- Fixed UI bugs
- BuildUvMesh call was not passing all parameters
- Changed Scale to Size, as the Size parameters represents the size in Unit Worlds of the
UVW Mesh
- Fixed bug where function "GetRealScale" won't work for Editable Polys
/////////////////////////////////////////////////////////////////////////////////////////////////
Description: This tool will help you create a mesh from an object UVW Channel.
*Fix Elements:
If the number of UVW elements is different than the number of the
object elements, it can reconstruct the source object to match the
number of UVW elements.
*Real Scale:
User can now choose between custom scale input value (default 100) or auto "real" scale value
which approximately matches polygon size.
/////////////////////////////////////////////////////////////////////////////////////////////////
*/
try (destroydialog ::RO_UVW_MESH) catch()
rollout RO_UVW_MESH "UVW Mesh v0.4" width:136 height:180
(
local sourcenode
pickbutton bt_pick "Pick Object" pos:[8,8] width:120 height:24
dropDownList ddl_channel "UVW Channel:" pos:[8,35] width:120 height:40 enabled:false
checkbox chk_rs "Use Real Scale" pos:[8,78] width:120 height:16 checked:true enabled:false
spinner spn_size "Size: " pos:[8,100] fieldwidth:81 range:[0.1,1e5,100.0] type:#worldunits enabled:false
checkbox chk_fix "Fix Elements" pos:[8,120] width:120 height:16 checked:true enabled:false
button bt_build "Build" pos:[8,140] width:120 height:32 enabled:false
fn GetRealScale node channel =
(
local v1 = getVert node 1
local v2 = getVert node 2
local mv1 = meshop.getmapvert node channel 1
local mv2 = meshop.getmapvert node channel 2
(distance v1 v2)/(distance mv1 mv2)
)
fn BuildUvMesh node channel:1 fixelements:false size:100.0 realscale:true =
(
local m1 = snapshotasmesh node
local numtverts = meshop.getnummapverts m1 channel
local getmapvert = meshop.getmapvert
local getmapface = meshop.getmapface
size = if realscale then GetRealScale m1 channel else size
local verts = for j = 1 to numtverts collect (getmapvert m1 channel j)*[size,size,0]
meshop.setnumverts m1 numtverts
meshop.setvert m1 #{1..numtverts} verts
for j = 1 to m1.numfaces do
(
face = getmapface m1 channel j
setface m1 j face[1] face[2] face[3]
)
if fixelements do
(
m2 = snapshotasmesh node
m3 = copy m1
for j = 1 to m1.numfaces do
(
f1 = getface m1 j; f2 = getface m2 j
setvert m3 (f1[1]) (getvert m2 f2[1])
setvert m3 (f1[2]) (getvert m2 f2[2])
setvert m3 (f1[3]) (getvert m2 f2[3])
)
mesh mesh:m3
delete m2
delete m3
)
mesh mesh:m1
delete m1
gc light:true
)
fn UpdateUI flag arg items:#() =
(
ddl_channel.enabled = chk_fix.enabled = bt_build.enabled = chk_rs.enabled = spn_size.enabled = flag
bt_pick.caption = arg; ddl_channel.items = items
if flag do spn_size.enabled = not chk_rs.checked
)
on chk_rs changed state do spn_size.enabled = not state
on bt_pick picked node do
(
if isKindOf node GeometryClass do
(
m = snapshotasmesh node
channels = for j = 1 to meshop.getnummaps m where (meshop.getmapsupport m j) collect j
if channels.count > 0 then
(
UpdateUI true node.name items:(for j in channels collect j as string)
sourcenode = node
)else(
messagebox (node.name + " has no UVW channels.") title:"UVW Channels Not Found"
UpdateUI false "Pick Object"
)
)
)
on bt_build pressed do
(
if isdeleted sourcenode == false then
(
setwaitcursor()
uvwchannel = ddl_channel.selected as integer
fix = chk_fix.checked
rs = chk_rs.checked
size = spn_size.value
BuildUvMesh sourcenode channel:uvwchannel fixelements:fix size:size realscale:rs
if fix do hide sourcenode
setarrowcursor()
)else(
messagebox (bt_pick.caption + " was deleted") title:"Node Deleted"
UpdateUI false "Pick Object"
sourcenode = undefined
)
)
)
createdialog RO_UVW_MESH style:#(#style_toolwindow, #style_sysmenu)
)
After thinking about the function to approximate the real size for the UVW mesh GetRealScale(), I think it wont always work as expected, because the geometry vertices and uvw vertices indices are not always the same.
The only thing that remains the same between geometry and uvw are the faces indices, so we could probably use the face 1 area of both to calculate the scale factor. This also has a problem, in case that face 1 (either geo or uvw) is highly distorted compared to the other faces. In this case the scale would be inaccurate.
Finally, another possibility could be to scale the uvw mesh after its creation and matching its full area with the geometry area.
For now, as the tool was meant to be used with MD meshes, I think using the face area will work just fine, but it wont be the case for a generic solution.
Ill try to look into deeper it and see what could be better.
Here is a different approach which seems to be very solid so far.
(
/*
Jorge Rodríguez
jorge@polytools3d.com
Version 0.1 | 08.18.12
Version 0.2 | 02.08.14
+ Added Fix Elements Option ( http://forums.cgsociety.org/showthread.php?f=98&t=1153097)
Version 0.3 | 02.11.14
+ Added Real Scale Option
Version 0.4 | 02.11.14
- Fixed UI bugs
- BuildUvMesh call was not passing all parameters
- Changed Scale to Size, as the Size parameters represents the size in Unit Worlds of the
UVW Mesh
Version 0.5 | 02.12.14
- Changed the code for calculating the "real scale" of the uvw mesh
/////////////////////////////////////////////////////////////////////////////////////////////////
Description: This tool will help you create a mesh from an object UVW Channel.
*Fix Elements:
If the number of UVW elements is different than the number of the
object elements, it can reconstruct the source object to match the
number of UVW elements.
*Real Scale:
User can now choose between custom scale input value (default 100) or auto "real" scale value
which approximately matches polygon size.
/////////////////////////////////////////////////////////////////////////////////////////////////
*/
try (destroydialog ::RO_UVW_MESH) catch()
rollout RO_UVW_MESH "UVW Mesh v0.5" width:136 height:180
(
local sourcenode
pickbutton bt_pick "Pick Object" pos:[8,8] width:120 height:24
dropDownList ddl_channel "UVW Channel:" pos:[8,35] width:120 height:40 enabled:false
checkbox chk_rs "Use Real Scale" pos:[8,78] width:120 height:16 checked:true enabled:false
spinner spn_size "Size: " pos:[8,100] fieldwidth:81 range:[0.1,1e5,100.0] type:#worldunits enabled:false
checkbox chk_fix "Fix Elements" pos:[8,120] width:120 height:16 checked:true enabled:false
button bt_build "Build" pos:[8,140] width:120 height:32 enabled:false
fn BuildUvMesh node channel:1 fixelements:false size:1.0 realscale:true =
(
local m1 = snapshotasmesh node
local numtverts = meshop.getnummapverts m1 channel
local getmapvert = meshop.getmapvert
local getmapface = meshop.getmapface
local verts = for j = 1 to numtverts collect (getmapvert m1 channel j)*[size,size,0]
local geoarea = meshop.getfacearea m1 #{1..m1.numfaces}
meshop.setnumverts m1 numtverts
meshop.setvert m1 #{1..numtverts} verts
for j = 1 to m1.numfaces do
(
face = getmapface m1 channel j
setface m1 j face[1] face[2] face[3]
)
if fixelements do
(
m2 = snapshotasmesh node
m3 = copy m1
for j = 1 to m1.numfaces do
(
f1 = getface m1 j; f2 = getface m2 j
setvert m3 (f1[1]) (getvert m2 f2[1])
setvert m3 (f1[2]) (getvert m2 f2[2])
setvert m3 (f1[3]) (getvert m2 f2[3])
)
mesh mesh:m3
delete m2
delete m3
)
if realscale == true do
(
uvwarea = meshop.getfacearea m1 #{1..m1.numfaces}
factor = sqrt (geoarea/uvwarea)
scale m1 [factor,factor,0]
)
mesh mesh:m1
delete m1
gc light:true
)
fn UpdateUI flag arg items:#() =
(
ddl_channel.enabled = flag
chk_fix.enabled = flag
bt_build.enabled = flag
chk_rs.enabled = flag
spn_size.enabled = flag
bt_pick.caption = arg
ddl_channel.items = items
if flag do spn_size.enabled = not chk_rs.checked
)
on chk_rs changed state do spn_size.enabled = not state
on bt_pick picked node do
(
if isKindOf node GeometryClass do
(
m = snapshotasmesh node
channels = for j = 1 to meshop.getnummaps m where (meshop.getmapsupport m j) collect j
if channels.count > 0 then
(
UpdateUI true node.name items:(for j in channels collect j as string)
sourcenode = node
)else(
messagebox (node.name + " has no UVW channels.") title:"UVW Channels Not Found"
UpdateUI false "Pick Object"
)
)
)
on bt_build pressed do
(
if isdeleted sourcenode == false then
(
setwaitcursor()
uvwchannel = ddl_channel.selected as integer
fix = chk_fix.checked
rs = chk_rs.checked
size = spn_size.value
BuildUvMesh sourcenode channel:uvwchannel fixelements:fix size:size realscale:rs
if fix do hide sourcenode
setarrowcursor()
)else(
messagebox (bt_pick.caption + " was deleted") title:"Node Deleted"
UpdateUI false "Pick Object"
sourcenode = undefined
)
)
)
createdialog RO_UVW_MESH style:#(#style_toolwindow, #style_sysmenu)
)
You can allow the user to choose between the different scale factor algorythms. If one of them fails, the user will try the next until he gets the proper result.
It could be, but I am afraid the final user may not know how to verify both meshes, unless he/she implements another code to measure the meshes areas.
In my opinion, the best way to verify if the meshes have the same (or similar) scale is to measure both surfaces areas.
The code I added in the version 0.5 does this; just scale the uvw mesh to match the geometry mesh. I cant see a situation where this method would be less accurate than the others.
These are the issues with the methods described so far:
#Arbitrary edge length
Vertex indices are not the same between geo and uvw meshes
Even if vertices are the same, the edge could be highly distorted compared with the other edges
#Arbitrary face area
Face could be highly distorted compared with the other faces
The scale of “flat” object need to be approximately exact but not perfectly exac.
Why? Because this object will be reference for newly retopologized object. When scale is similar user can easily decide which size of quad need to be used for retopology process.
But why use a method that we already know can fail when there is another that is more accurate and does not have any drawbacks?
This one I added in v 0.5 ( http://forums.cgsociety.org/showpost.php?p=7753370&postcount=31 ) Which uses the total object surface area to scale the UVW Mesh
uvwarea = meshop.getfacearea m1 #{1..m1.numfaces}
factor = sqrt (geoarea/uvwarea)
scale m1 [factor,factor,0]
Here is a test case. After running it use version 0.4 and 0.5 of the UVW Mesh script to get the uvw mesh using the Real Scale option.
v0.4 – Uses arbitrary edge length
v0.5 – Uses geometry surface area
(
delete objects
node = converttopoly (teapot isSelected:true)
uvwmod = unwrap_uvw()
modpanel.addmodtoselection uvwmod ui:on
uvwmod.flattenmap 60.0 #() 0.01 true 0 false true
)
looking at your development process i found helpful make this function for you:
fn uvToMesh node channel:1 scaleFactor:1.0 =
(
_mesh = snapshotasmesh node
sc = point3 scaleFactor scaleFactor scaleFactor
verts = for v=1 to meshop.getnummapverts _mesh channel collect (meshop.getmapvert _mesh channel v)*sc
faces = for f=1 to meshop.getnummapfaces _mesh channel collect meshop.getmapface _mesh channel f
mesh name:(node.name + "_uvmesh") vertices:verts faces:faces
)
delete objects
b = box()
modi = Unwrap_UVW()
addmodifier b modi
max modify mode
select b
modpanel.setcurrentobject modi
modi.flattenMapNoParams()
max create mode
clearselection()
addmodifier b (tessellate tension:0 iterations:3)
addmodifier b (tessellate tension:0 iterations:1)
gc light:on
t1 = timestamp()
uvToMesh b scaleFactor:100.0
format "verts:% time:%
" b.numverts (timestamp()-t1)
edited… i’ve added tessellation just to show how fast it is. and of course the function need some standard optimization steps and a fool-proofing.
Ice-boy sended me this file for test (max2011 file).
Denis your code breaks down model into small pieces.
Jorge already have nice and clean solution.
it doesn’t brake anything.
@Jorge: put meshop functions in my function into locals. as usually i do it. it saves the memory.
If I do so, it would no longer be a cute 5 lines code
Anyway, while speed and memroy ussage are pretty similar, the other two issues still remains (triangulation and uvw map shift).
Thats another way of doing the same thing (almost). The performance on my end is around 25% faster and it uses around 6 times more memory for your test box.
faces:49152 time:96 memory:9567184L
faces:49152 time:118 memory:1652344L
For larger meshes the speed is almost the same but it still uses 6 times more memory
faces:196608 time:1155 memory:37990640L
faces:196608 time:1209 memory:6420120L
So, it does not have a substantial improving regarding speed and it uses much more memory.
Other issues I see are that the output mesh is triangulated, and its mapping if offset.
It also lacks the option to split your source mesh based on the different uv shells, which was the main purpose of my script.
according my tests my function works ~3 times faster… memory… yes. it uses. because it makes arrays.
I wonder… The results on my tests are as I previously mentioned. I suppose you are testing both functions together (mine first) without rebuilding the whole test case.
In that case the function you test at last, will appear to be faster. But try the following test and put back your result please.
(
fn uvToMesh node channel:1 scaleFactor:1.0 =
(
_mesh = snapshotasmesh node
sc = point3 scaleFactor scaleFactor scaleFactor
verts = for v=1 to meshop.getnummapverts _mesh channel collect (meshop.getmapvert _mesh channel v)*sc
faces = for f=1 to meshop.getnummapfaces _mesh channel collect meshop.getmapface _mesh channel f
mesh name:(node.name + "_uvmesh") vertices:verts faces:faces
)
fn BuildUvMesh node channel:1 fixelements:false size:1.0 realscale:true =
(
local m1 = snapshotasmesh node
local numtverts = meshop.getnummapverts m1 channel
local getmapvert = meshop.getmapvert
local getmapface = meshop.getmapface
local verts = for j = 1 to numtverts collect (getmapvert m1 channel j)*[size,size,0]
meshop.setnumverts m1 numtverts
meshop.setvert m1 #{1..numtverts} verts
for j = 1 to m1.numfaces do
(
face = getmapface m1 channel j
setface m1 j face[1] face[2] face[3]
)
mesh mesh:m1
delete m1
gc light:true
)
for nn = 1 to 2 do
(
delete objects
b = box()
modi = Unwrap_UVW()
addmodifier b modi
max modify mode
select b
modpanel.setcurrentobject modi
modi.flattenMapNoParams()
max create mode
clearselection()
addmodifier b (tessellate tension:0 iterations:3)
addmodifier b (tessellate tension:0 iterations:1)
gc()
st = timestamp(); sh = heapfree
if nn == 1 then uvToMesh b scaleFactor:100.0
else BuildUvMesh b size:100.0
format "faces:% time:% memory:%
" b.numfaces (timestamp()-st) (sh-heapfree)
)
)
I’ve tested with 3 and 4 iterations on this line:
addmodifier b (tessellate tension:0 iterations:3)
3 Iterations
faces:49152 time:94 memory:9569776L
faces:49152 time:118 memory:1654936L
4 Iterations
faces:196608 time:1174 memory:37993360L
faces:196608 time:1225 memory:6422696L
it doesn’t do anything with triangulation… about uv map i just don’t care and do nothing. but it’s very easy to fix