'Hard Edges' in the Bake texture maps thingy is giving me unexpected results.
The edges in the render are offset, and doesn't match the UVs...also, the lowest thickness (1) is way too fat, and my model is to scale (if that matters, it's a tank)
I use this script all the time and it's amazing! But you know, I can never get "Rectify" to work. It looks like I should be able to select UV islands made up of quads and have them get straightened into rectangles but it never does anything when I left click the button. Is this a known thing or is there a hidden 'gotcha' that I'm not aware of?
ya i have seen that too with rectify. I'll select a element that is fairly close to rectangular in the first place. click rectify and it will usually ball it all up and make a huge mess. Crtl-z
Also can't get the linear align to work now in 2011. It never quite straightens it, always have to use the old scaling method again to get lines straight.
Still an amazing tool overall and can't live without it.
'Hard Edges' in the Bake texture maps thingy is giving me unexpected results.
The edges in the render are offset, and doesn't match the UVs...also, the lowest thickness (1) is way too fat, and my model is to scale (if that matters, it's a tank)
I've been wondering myself how this works, any tips would be great!
maybe you can implement something from this stuff.. I'd like to see transfer of UV from hipoly to lowpoly for example.. missing this
-- UV Layout
-- This program was split off from the Surface Toolkit.... Thanks to the guy who coded the TV drawing routine I'm using.
-- please email me at vajuras@planetunreal.com so I can give ya credit. www.cyberkreations.com/kreationsedge
-- Updated May 9, 2001 to work in Max 4. I think my interest in this was rekindled so that it could be used to help Chilliskinner advance.
-- On jan. 23 the unwrap algorithm was vastly improved to handle unwraps on the right
-- and left side. Also, added Match UVW function/utility.
-- 2/7
-- Made the Camera Projection Logic.
-- 2/14
-- Improved "Update Map" routine which would have an error.
-- Render_map will now convert object to mesh if not an Editable Object.
-- Added ability to separately render TVFaces without splitting a mesh object. This
-- method seems to work much better and return more accurate results...
-- 2/20
-- Update_Texture now works with Multi-materials...
-- 2/23
-- started working on the Skin Composer routine.
-- 3/02
-- Actually put in the AI for Blender.
-- Upgraded the UV Manager to create an alpha channel for uv maps.
-- Blender uses advanced algorithm to compute across any range [n, n] instead of [0, 100].
-- This is the code that does the math:
-- dist = abs(sort_gy - sort_ly)
-- pnt = 1/dist
-- p_dist = abs(pix.y - sort_ly)
-- blend_pnt = p_dist*pnt
-- 3/12
-- Added Attachments Utility
-- 4/17
-- Fixed a bug where the VertexSelection did not work for SurfUnwrap.
-- v1.2
-- 4-21
-- Added Edge Colors & Surface Glue.
-- Extended the range of the size of an image.
-- The Script used to crash if the restore 0r Apply under Project UVW button was pressed.
-- You can now enter float values for the Width & Length of a bitmap (UV Manager)
-- A lot of easy crashes were fixed that may be caused if a user clicks the wrong button.
-- v1.2.2
-- Fixed a crash that may occur during a 'render map' when the color comes up undefined...
-- Default Material checkbox will now go & load the Material's diffusemap. Changes a BitmapTexture to Bitmap.
-- Increased User Feedback
-- v1.2.3 -> v1.2.4
-- Fixed the bug in which the edge color array would crash the utility.
-- The utility displays the expiration date.
-- v1.3
-- Added UV Mesher Tool.
-- Improved Glue Tool Performance.
-- v1.3.3
-- UV Mesher grabs the dimension of the Diffuse Channel Bitmap if available.
-- WARP feature for the UV Mesher was fixed.
-- v1.4
-- UpdateMap feature now reloads all the mapping channels instead of just the diffuse channel.
utility surfHelp "UV Layout"
(
local sort_y, morph_state = false
-- stores the selection of cross objects
local b_cross = #()
local b_crossNum = 0
local s_width = 190
local s_height = 345
-- For the 3D Paint Options
local default_secs = 3600
local map_type = 0 --default mapping: planar
local uvw_axis = 0
-- Emergency ShutDown
local shutDown = false
-- Determines if this script is registed, if so, all features are enabled.
local register = true
label display_global ""
--stores color for each edge (UV Layout & Edge)
local color_mat = #()
-- global user_color
local global_color = red
-- selection set data
local sel_set =#(), sel_index = 1
-- Variables for GLUE
local glue_set = #()
local glue_names = #()
local glue_objA = #()
local glue_objB = #()
local glue_pos = #(#())
local previous_b = #() -- Stores the original OBJECT B
-- Variables for UV Mesher
local face_set = #() -- Stores the origin for the TVFace.
fn grab_sel =
(
selection as array
)
-- Selects all the faces in a mesh.
fn setFaceArray tar numF =
(
tar = #()
for i = 1 to numF do
(
tar[i] = i
)
return tar
)
-- Retrieves the date from a string and returns it in a 3-element array. The string must conform to localTime String Conventions
-- meaning a space character must be included like "11/23/98 8:23PM". This function can also tokenize a regular string.
-- This routine automatically looks for / and \\ (backslash) but, an extra delimeter can be searched.
-- By default, you should send a space character.
fn get_all_tokens str_in delimeter =
(
tokens = #()
str = str_in as string
len = str.count
count = 1 -- Count the # of tokens
elmo = 0 -- Count valid charcters
for a = 1 to len do
(
if str[a] == "/" or str[a] == delimeter or str[a] == "\\" then
(
this_token = substring str count elmo
append tokens this_token
-- Set the pointer to the beginning of the next slot.
count = a + 1
elmo = 0
)
else
(
-- Count the valid characters
elmo = elmo + 1
)
)
if shutDown == true then
(
shutDown == true
return #()
)
-- Return the last token also.
this_token = substring str count elmo
append tokens this_token
tokens
)
-- Replace all occurances of a character with another.
fn replace_char str delimeter new_char =
(
len = str.count
for a = 1 to len do
(
if str[a] == delimeter then str[a] = new_char
)
str
)
-- Opt 1: Encrypt
-- 2: Not Encrypted
fn read_data file_name opt =
(
count = 1
j_name = #()
if opt == 2 then file = openFile (file_name)
else
file = openEncryptedFile (file_name) 252525
if file != undefined then
(
while (not eof file) do
(
str = readline file
j_name[count] = str
count = count + 1
)
close file
) -- if
j_name
)
-- Opt 1: encrypt
-- 2: not encrypt
fn save_data file_name path_string opt =
(
if file_name != undefined then
(
if opt == 1 then file = createFile(file_name+".tmp")
else
file = createFile(file_name)
format "%\n" path_string to:file
close file
) -- if
if opt == 1 then (
encryptFile (file_name+".tmp") file_name 299525
deleteFile (file_name+".tmp")
)
)
-- Use the .ini file to determine where the MAX ROOT is.
fn get_root =
(
f = read_data "uv_max/layout.ini" 2
if f.count < 1 then (
messagebox "Please press the Online Help (.chm) button first to install the .ini file before continuing."
return #()
)
tokens = get_all_tokens f[1] "-"
tokens
) -- warez
-- This function will return another point that lies in the same spline. This func is called by the unwrap_spline
-- func which in turn uses this AI to help determine which xAxis to assign to a spline knot. The Spline Ends Algorithm
-- is applied which states that the very next vertex in the spline order can be used to determine the direction of the
-- knot.
fn findOtherKnot obj spl vert =
(
local pnt
numK = numKnots obj spl
old_pnt = getKnotPoint obj spl vert
next_vert = vert - 1
if vert >1 then next_vert = vert - 1
else
next_vert = vert + 1
pnt = getKnotPoint obj spl next_vert
gw = "NEXT:"+next_vert as string+" SENT:"+vert as string
-- Return the same point back if no vert exists on a different xAxis in the same spline.
pnt
)
-- This function will return another point that lies in the same spline. This func is called by the radialSolver
-- func which in turn uses this AI to help determine which xAxis to assign to a spline knot.
fn simpleFindOtherKnot obj spl vert =
(
local pnt
numK = numKnots obj spl
old_pnt = getKnotPoint obj spl vert
for i = 1 to numK do
(
pnt = getKnotPoint obj spl i
if old_pnt.x != pnt.x then return pnt
)
-- Return the same point back if no vert exists on a different xAxis in the same spline.
pnt
)
-- This function makes any knot that equal the deformed knot move also similar to the Global Transform Func.
-- Since the new position is along the yAxis, it will not be deformed again.
-- Var:
-- new_pos - new position where this vert should move
fn makeAllKnotsEqual obj spl vert new_pos thres =
(
nums = numSplines obj
old = getKnotPoint obj spl vert -- current position of the before the new move.
for i = 1 to nums do
(
numk = numKnots obj i
for j = 1 to numk do
(
pnt = getKnotPoint obj i j
-- Exclude the spline where the original knot came from. Spline Ends Algorithm.
pntxUp = old.x + thres
pntxDown = old.x - thres
pntyUp = old.y + thres
pntyDown = old.y - thres
pntzUp = old.z + thres
pntzDown = old.z - thres
if i != spl and (pnt.x <= pntxUp and pnt.x >= pntxDown) and (pnt.y <= pntyUp and pnt.y >= pntyDown) and (pnt.z <= pntzUp and pnt.z >= pntzDown) then
(
pnt = new_pos
setKnotPoint obj spl vert pnt
)
)
)
)
-- This function will solve for a radial b-spline since the Spline Ends Algor. fails if the points lie within the
-- threshold.
fn radialSolver obj spl vert =
(
numS = numSplines obj
new_spline = spl - 1
-- limit checks
if new_spline < 1 then new_spline = spl + 1
if new_spline > numS then new_spline = spl
pnt = simpleFindOtherKnot obj new_spline vert
-- Return another point on another spline that can be used to determine the direction.
pnt
)
-- This function unwraps a spline object. If the surf is cylindrical/spherical, the knots along the mid spline behind the base axis
-- will be pulled apart. Spline Ends Algorithm examines a spline and will assign to the middle knot automatically the direction
-- the knot should go which is naturally, determined by the Order of the spline.
-- b_a - Y axis
-- x_a - X axis
fn unwrap_spline obj b_a x_a thres output =
(
local num_s, num_k, val, pnt, gw
dist = 0.0 -- distance pnt from x_a
try (
num_s = numSplines obj
) catch (
messagebox "Please choose a valid object."
return 0
)
for i = 1 to num_s do
(
num_k = numKnots obj i
calcy = (i as float / num_s as float) * 100
calcy = calcy as integer
lc = calcy as string + "%"
output.text = lc
for j = 1 to num_k do
(
pnt = getKnotPoint obj i j
gw = i as string + ":" + j as string + " "+pnt as string
-- If the point is below the base axis, adjust the val variable.
if pnt.y > b_a then val = pnt.y - b_a
else
val = 0
ny = pnt.y
nx = pnt.x
dist = (abs(x_a - pnt.x))/100
-- New Y coordinate will be at base axis. If Y coordinate is less than base axis, then let it keep its position.
if pnt.y > b_a then
(
ny = b_a
--nx = pnt.x - val
--gw = "Behind base "+i as string+":"+j as string+" "+pnt.x as string
--print gw
-- If the point is along the middle spline behind the base axis.... Determine which direction to move.
midUp = (x_a as float + thres as float)
midDown = (x_a as float - thres as float)
if pnt.x <= midUp and pnt.x >= midDown then
(
otherKnot = findOtherKnot obj i j
--gw = "Mid "+i as string+":"+j as string
--print gw
-- If the Spline Ends Algorithm has failed in case of radial b-splines then, use the radial b-solver algorithm
-- instead.
if otherKnot.x == pnt.x then otherKnot = radialSolver obj i j
est = otherKnot.x
if est < (x_a) then nx = pnt.x - (val * dist)
else
nx = pnt.x + (val * dist)
-- Now, deform any other colliding vertices.... The deformed verts will not ever be deformed again because
-- they will equal the b_a.
new_pnt = [nx, ny, pnt.z]
makeAllKnotsEqual obj i j new_pnt thres
)
else
(
if pnt.x < x_a then nx = pnt.x - (val * dist)
else
(
if pnt.x > x_a then
(
nx = pnt.x + (val * dist)
gw = gw + "RIGHT "
new_pnt = [nx, ny, pnt.z]
--makeAllKnotsEqual obj i j new_pnt thres
) -- if
) -- if
) -- if
) -- if
setKnotPoint obj i j [nx, ny, pnt.z]
updateShape obj
--gw = gw + " "+nx as string + ":" + ny as string + " " + val as string
--print gw
)
)
try (
) catch (
messagebox "Please select a valid object"
return 0
)
output.text = "->"
)
-- This is a general func designed to work with any type of surface. It
-- takes the object and computes the highest or lowest x value.
-- option 1: largest, opt 2: least
fn compute_percent obj opt =
(
local sort = 0, diff_x = 0.0, sort_x = 0
convertToMesh obj
numS = getnumverts obj
for i = 1 to numS do
(
pnt = getvert obj i
if i == 1 then (
diff_x = pnt.x
sort_x = i
)
else
(
if opt == 1 and pnt.x > diff_X then
(
sort_x = i
diff_x = pnt.x
)
if opt != 1 and pnt.x < diff_X then
(
sort_x = i
diff_x = pnt.x
)
)
)
sort_x
)
-- This function unwraps a mesh object. The val is computed to determine the total
-- distance a point should be moved along the X axis. The dist is computed as the
-- distance the point is from the X axis which gives a percentage value.
-- b_a - Y axis
-- x_a - X axis
fn unwrap_mesh obj b_a x_a x_width use_stack disp =
(
local num_s, num_k, val, pnt, gw, target = #(), i, sort_x
dist = 0.0 -- distance pnt.x is from x_a divided by 100 to get
-- a percentage. This computation will then be used
-- to determine how far from the X axis the points
-- should be deformed.
count = 1
select_i = 0 -- turns on if stack is present...
num_verts = 0
try (
dummy = getnumfaces obj
) catch (
convertToMesh obj
)
--Had to take out unwrap support for a vertex selection. It was a useless feature anyway...
num_verts = getnumverts obj
if select_i == 0 or use_stack == false then
(
for i = 1 to num_verts do
(
target[i] = i
)
) -- if
disp.text = "Analyzing Topology...."
sort_x = compute_percent obj 1
far_vert = getvert obj sort_x
sort_neg = compute_percent obj 2
least_vert = getvert obj sort_neg
-- Get the percentage from the farthest vert. Then, this value will be used
-- to equate the values to a range [low, 100]
percent = 100 / (abs(x_a - far_vert.x))
percent_neg = 100 / (abs(x_a - least_vert.x))
progressStart "Unwrapping..."
cond = true
count = 1
for i in target do
(
calcy = (i as float / num_verts as float) * 100
calcy = calcy as integer
lc = calcy as string + "%"
if select_i == 1 then disp.text = "Using Stack Selection...."
else
cond = progressUpdate calcy
if cond == false then return 0
try (
pnt = getvert obj i
) catch (
pnt = getvert obj count
)
-- If the point is below the base axis, adjust the val variable.
if pnt.y > b_a then val = pnt.y - b_a
else
val = 0
ny = pnt.y
nx = pnt.x
if pnt.x < x_a then dist = (abs(pnt.x - x_a)) * percent_neg
else
if pnt.x > x_a then dist = (abs( pnt.x - x_a)) * percent
dist = dist / 100
-- If the user wants to morph the mesh, then let dist always eq. 1 so
-- that the verts may be deformed.
--if morph_state == true then dist = 1
-- New Y coordinate will be at base axis. If Y coordinate is less than base axis, then let it keep its position.
if pnt.y > b_a then
(
ny = b_a
--nx = pnt.x - val
-- Include the middle spline as well
-- If midpoint is from LEFT TO RIGHT then mid = x_a + 1
--mid = x_a + 1
if pnt.x < x_a then
nx = pnt.x - (val * dist)
else
(
if pnt.x > x_a then (
nx = pnt.x + (val * dist)
)
total = val * dist
--gw = "Dist:"+dist as string+" Val:"+val as string+" Total:"+total as string
--print gw
)
) -- if
try (
setvert obj i [nx, ny, pnt.z]
) catch (
setvert obj count [nx, ny, pnt.z]
)
--gw = gw + " X:Y "+nx as string + ":" + ny as string + " V:" + val as string
--print gw
count = count + 1
)
update obj
disp.text = ""
progressEnd()
-- Return target array.
target
)
-- This function uses the array (spl) to restore the original target.
fn restore_spline obj spl output =
(
local token = #()
local count = 1
if classof obj != editable_mesh then return
try (
lengt = spl.count
for i = 1 to lengt do
(
calcy = (i as float / lengt as float) * 100
calcy = calcy as integer
lc = calcy as string + "%"
output.text = lc
token = get_token spl[i] " "
setKnotPoint obj (token[1] as Number) (token[2] as Number) [token[3] as float, token[4] as float, token[5] as float]
)
) catch (
messagebox "Cannot restore shape."
return 0
)
output.text = "->"
updateShape obj
)
fn restore_mesh obj target spl output =
(
leng = target.count -- Get the Vertex Selection
real_num = getnumverts obj -- Get # of verts in the mesh object
count = 0
collapseStack obj
if leng < 1 then
(
-- Fill target array
for i = 1 to real_num do
(
count = count + 1
target[count] = getvert obj i
)
)
count = 0
progressStart "Restoring..."
for i in target do
(
count = count + 1
calcy = (count as float / leng as float) * 100
calcy = calcy as integer
progressUpdate calcy
-- An empty target array means No Vertex Selection was made.
if leng == real_num or leng < 1 then setvert obj count spl[count]
else
setvert obj i spl[i]
--gw = "Restore:"+spl[count] as string
)
try (
--output.text = "->"
progressEnd()
) catch ()
update obj
)
-- This func draws the pixels in-between the 2 points.
fn draw_line x1 y1 x2 y2 uvBMP u_color =
(
pnt = #()
-- Compute the pixels between 2 points. First, derive the distance.
dist_x = abs(x2 - x1)
dist_y = abs(y2 - y1)
if x1 > x2 then l_x = x2
else
l_x = x1
if y1 > y2 then l_y = y2
else
l_y = y1
-- Let i take the value of y
for i = 1 to (dist_y) do
(
-- move right
diff_x = x2 - x1
diff_y = y2 - y1
pnt[i] = [0,0]
if diff_x > 0 then pnt[i].x = diff_x
if diff_y > 0 then pnt[i].y = diff_y
)
)
-- Bresenham line implimentation for MAXScript
-- by Harry Denholm, Kinetix 1998
function BLine bmp x1 y1 x2 y2 c old_color =
(
-- stick the colour into an array
local ary = c
if ary == undefined then ary = #(old_color)
-- assign originals
local Xb = x1 as integer
local Yb = y1 as integer
-- build the line deltas
local dX = x2-x1 as float
local dY = y2-y1 as float
-- straight horiz
if dy == 0.0f do (
local xsign = 1
if xb > x2 then xsign = 1 as integer
if x2 < xb then xsign = -1 as integer
setPixels bmp [xb,yb] ary
while xb != x2 do
(
xb += xsign
setPixels bmp [xb,yb] ary
)
return true
)
-- straight vertical
if dx == 0.0f do (
local ysign = 1
if yb > y2 then ysign = 1 as integer
if y2 < yb then ysign = -1 as integer
setPixels bmp [xb,yb] ary
while yb != y2 do
(
yb += ysign
setPixels bmp [xb,yb] ary
)
return true
)
-- no straights, go for bresenham line slide
-- set up the movements
local xsign = 1
if xb > x2 then xsign = 1 as integer
if x2 < xb then xsign = -1 as integer
local ysign = 1
if yb > y2 then ysign = 1 as integer
if y2 < yb then ysign = -1 as integer
dx = abs(dx)
dy = abs(dy)
setPixels bmp [xb,yb] ary
-- line more vertical than horizontal
if dx < dy then
(
p = 2 * dx - dy
const1 = 2 * dx
const2 = 2 * (dx - dy)
while yb != y2 do
(
yb += ysign
if p < 0 then ( p = p + const1 )
else
(
p += const2
xb += xsign
)
setPixels bmp [xb,yb] ary
)
)
-- line more horizontal than vertical
else
(
p = 2 * dy - dx
const2 = 2 * (dy - dx)
const1 = 2 * dy
while xb != x2 do
(
xb = xb + xsign
if p < 0 then ( p = p + const1 )
else
( p = p + const2
yb = yb + ysign
)
setPixels bmp [xb,yb] ary
)
)
)
-- Set material color to the bitmap.
fn setMaterial uvBMP inMat =
(
iW = uvBMP.width
iH = uvBMP.height
inMat.alpha = 0.0
mat_color = #(inMat)
for i = 1 to iW do
(
for j = 1 to iH do
(
setPixels uvBMP [i,j] mat_color
)
)
)
-- This function renders the unwrapped surface or face selection.
-- opt - use face selection from stack.
fn render_uvw obj uvBMP u_color opt =
(
target = #()
try (
iW = uvBMP.width
iH = uvBMP.height
) catch (
messagebox "Please select a bitmap or uncheck the Include Bitmap option."
return 0
)
status = false
count = 0
edge_num = 0
t_count = getnumfaces obj
Uobj = obj
cCol = newCol = u_color
display_global.text = "Rendering UVW Coordinates..."
progressStart "Rendering..."
-- Always assume that a UVWmap mod is at the top so, grab the next mod.
try (
target = getFaceSelection Uobj Uobj.modifiers[1]
) catch (
try (
target = getFaceSelection Uobj Uobj.modifiers[2]
) catch (
-- Set the func. to use all the faces in the mesh.
opt = false
) -- catch
) -- catch
gw = "Faces:"+t_count as string+" "+target as string
print gw
if opt == false then (
target = #()
--delta = (100.0/(getnumfaces Uobj)) as float
for a = 1 to t_count do
(
target[a] = a
)
)
limit = target.count
for t in target do
(
-- update our progress bar
count = count + 1
calcy = (count as float / limit as float) * 100
calcy = calcy as integer
progressUpdate calcy
--progressUpdate ((t*delta) as integer)
status = getProgressCancel()
if shutDown == true then(
status == true
)
if status == true then (
progressEnd()
display_global.text = ""
return false
)
-- get the faces
faceN = getface Uobj t
faceT = getTVFace Uobj t
-- take the TVs out of the face index
tva = gettvert Uobj faceT.x
tvb = gettvert Uobj faceT.y
tvc = gettvert Uobj faceT.z
-- unwrap TVs. If a new Edge Color exists, use that color instead.
try (
edge_num = t * 3
newCol = #(color_mat[edge_num-2])
if color_mat[edge_num-2] == 0 then newCol = cCol
) catch (
newCol = cCol
)
if newCol == undefined then newCol = cCol
if (getedgevis Uobj t 1 and color_mat[edge_num-2] != -1) then \
Bline uvBMP (iW*tva.x) (iH*(1.0-tva.y)) (iW*tvb.x) (iH*(1.0-tvb.y)) newCol cCol
try (
edge_num = t * 3
newCol = #(color_mat[edge_num-1])
if color_mat[edge_num-1] == 0 then newCol = cCol
) catch (
newCol =cCol
)
if newCol == undefined then newCol = cCol
if (getedgevis Uobj t 2 and color_mat[edge_num-1] != -1) then \
Bline uvBMP (iW*tvb.x) (iH*(1.0-tvb.y)) (iW*tvc.x) (iH*(1.0-tvc.y)) newCol cCol
try (
edge_num = t * 3
newCol = #(color_mat[edge_num])
if color_mat[edge_num] == 0 then newCol = cCol
) catch (
newCol =cCol
)
if newCol == undefined then newCol = cCol
if (getedgevis Uobj t 3 and color_mat[edge_num] != -1) then \
Bline uvBMP (iW*tvc.x) (iH*(1.0-tvc.y)) (iW*tva.x) (iH*(1.0-tva.y)) newCol cCol
)
progressEnd()
display_global.text = ""
display uvBMP
)
-- This func copies the UVW coordinates and material assignments from one object
-- to another.
fn match_uvw target source deform_uv =
(
tmap = true -- texture mapping applied
t_equal = true -- if tverts equal then deform the texure vertices. If not,
-- simply assign the materials.
--convertToMesh target
--convertToMesh source
try (
a = getnumfaces target
b = getnumfaces source
) catch (
return 0
)
if a != b then (
messagebox "These objects have different face counts (topology)."
return 0
)
try (
c = getnumtverts target
d = getnumtverts source
) catch (
--messagebox "No Mapping Applied..."
--return 0
tmap = false
c = d = 0
)
if c < 1 or d < 1 then (
--messagebox "No Mapping Applied..."
--return 0
tmap = false
)
if c != d then (
--messagebox "The objects have different counts of texture vertices."
--return 0
t_equal = false
)
progressStart "Setting TVFaces...."
--convertToMesh source
-- copy materials
f = target.material
source.material = f
for i = 1 to a do
(
calcy = (a as float / i as float) * 100
calcy = calcy as integer
progressUpdate calcy
-- Copy separate face assignments if Multi-Object has been applied.
f = getfacematid target i
setfacematid source i f
if tmap == true and t_equal == true and deform_tv == true then (
f = getTVFace target i
t1 = gettvert target f.x
settvert source f.x t1
t2 = gettvert target f.y
settvert source f.y t2
t3 = gettvert target f.z
settvert source f.z t3
setTVFace source i f.x f.y f.z
)
)
progressEnd()
update source
)
-- Update Map
-- Updates a standard and multi-material's diffusemap.
fn update_texture obj junk_bitmap =
(
local file_name
count = 0 -- count # of updated textures
-- Grab the Current Selection to refresh the buffer.
obj_array = grab_sel()
obj = obj_array[1]
try (
classify = classof obj.material
) catch (
messagebox "Please select the object first!"
return false
)
if classify != Standardmaterial and classify != Multimaterial then
(
messagebox "Wrong Material Type! This utility only works with\n Standard & MultiMaterials."
return false
)
if classify == Standardmaterial then
(
try (
file_name = obj.material.diffusemap.filename
obj.material.diffusemap.filename = file_name
count = count + 1
update obj
freeSceneBitmaps()
) catch ()
try (
file_name = obj.material.bumpmap.filename
obj.material.bumpmap.filename = file_name
count = count + 1
update obj
freeSceneBitmaps()
) catch ()
try (
file_name = obj.material.opacitymap.filename
obj.material.opacitymap.filename = file_name
count = count + 1
update obj
freeSceneBitmaps()
) catch ()
try (
file_name = obj.material.filtermap.filename
obj.material.filtermap.filename = file_name
count = count + 1
update obj
freeSceneBitmaps()
) catch ()
try (
file_name = obj.material.reflectionmap.filename
obj.material.reflectionmap.filename = file_name
count = count + 1
update obj
freeSceneBitmaps()
) catch ()
try (
file_name = obj.material.refractionmap.filename
obj.material.refractionmap.filename = file_name
count = count + 1
update obj
freeSceneBitmaps()
) catch ()
try (
file_name = obj.material.selfillummap.filename
obj.material.selfillummap.filename = file_name
count = count + 1
update obj
freeSceneBitmaps()
) catch ()
try (
file_name = obj.material.shinestrengthmap.filename
obj.material.shinestrengthmap.filename = file_name
count = count + 1
update obj
freeSceneBitmaps()
) catch ()
try (
file_name = obj.material.shininessmap.filename
obj.material.shininessmap.filename = file_name
count = count + 1
update obj
freeSceneBitmaps()
) catch ()
try (
file_name = obj.material.specularmap.filename
obj.material.specularmap.filename = file_name
count = count + 1
update obj
freeSceneBitmaps()
) catch ()
try (
file_name = obj.material.ambientmap.filename
obj.material.ambientmap.filename = file_name
count = count + 1
update obj
freeSceneBitmaps()
) catch ()
if count > 0 then
messagebox (count as string +" Bitmap Texture(s) has been updated.")
)
if classify == Multimaterial then
(
-- what are the number of sub-materials?
mat_count = obj.material.numsubs
-- Set the loop to go through the obj.material array
for a = 1 to mat_count do
(
try (
file_name = obj.material[a].diffusemap.filename
obj.material[a].diffusemap.filename = file_name
count = count + 1
update obj
freeSceneBitmaps()
) catch ()
try (
file_name = obj.material[a].bumpmap.filename
obj[a].material.bumpmap.filename = file_name
count = count + 1
update obj
freeSceneBitmaps()
) catch ()
try (
file_name = obj.material[a].opacitymap.filename
obj.material[a].opacitymap.filename = file_name
count = count + 1
update obj
freeSceneBitmaps()
) catch ()
try (
file_name = obj.material[a].filtermap.filename
obj.material[a].filtermap.filename = file_name
count = count + 1
update obj
freeSceneBitmaps()
) catch ()
try (
file_name = obj.material[a].reflectionmap.filename
obj.material[a].reflectionmap.filename = file_name
count = count + 1
update obj
freeSceneBitmaps()
) catch ()
try (
file_name = obj.material[a].refractionmap.filename
obj.material[a].refractionmap.filename = file_name
count = count + 1
update obj
freeSceneBitmaps()
) catch ()
try (
file_name = obj.material[a].selfillummap.filename
obj.material[a].selfillummap.filename = file_name
count = count + 1
update obj
freeSceneBitmaps()
) catch ()
try (
file_name = obj.material[a].shinestrengthmap.filename
obj.material[a].shinestrengthmap.filename = file_name
count = count + 1
update obj
freeSceneBitmaps()
) catch ()
try (
file_name = obj.material[a].shininessmap.filename
obj.material[a].shininessmap.filename = file_name
count = count + 1
update obj
freeSceneBitmaps()
) catch ()
try (
file_name = obj.material[a].specularmap.filename
obj.material[a].specularmap.filename = file_name
count = count + 1
update obj
freeSceneBitmaps()
) catch ()
try (
file_name = obj.material[a].ambientmap.filename
obj.material[a].ambientmap.filename = file_name
count = count + 1
update obj
freeSceneBitmaps()
) catch ()
) -- loop
if count > 0 then
messagebox (count as string +" Bitmap Textures has been updated.")
) -- if
true
)
-- this func looks for the uvwmap mod in the modifier stack and then the modifier
-- is returned.
fn check_stack mod_name obj =
(
local mod_n
this_stack = obj.modifiers.count
for i = 1 to this_stack do
(
mod_n = obj.modifiers[i]
names = classof mod_n
if names == mod_name then exit
)
return mod_n
)
fn expire_script =
(
messagebox "This script is time-sensitive. Please make sure your system clock is functioning properly." title:"System Clock Error"
)
fn test_script =
(
token = #()
token = get_all_tokens localTime " "
if token[3] as integer < 0 then
(
print token[1]
expire_script()
return false
)
true
)
fn process_diff pntA pntB =
(
diffX = abs(pntA.x - pntB.x)
diffY = abs(pntA.y - pntB.y)
diffZ = abs(pntA.z - pntB.z)
mean = diffX + diffY + diffZ
mean
)
---------------------------------------------------------------------------------
-- UI
---------------------------------------------------------------------------------
-- SurfUnwrap
local unwrap_obj -- unwrap surface
local meshU_obj -- stores the 'target' array from the vertSelection
local object = true -- picked spline surf
local old_spl = #() -- store the original mesh/spline
local x_width = 0.0
local rPressed = 0 -- stores if restore button pressed
-- Junk Bitmap
local junk_bitmap = ""
-- Button Width
local b_width = 135, b_height = 18
rollout attach_roll "<-- Attachments -->"
(
local group_a, group_b
local attach_num = 0
local m_list = #()
group ""
(
pickbutton obj_a "Source" width:100 tooltip:"Source object"
pickbutton obj_b "Attach" width:100 tooltip:"Choose mesh to attach."
checkbox no_attach "Do not Attach" checked:false align:#center
button save_list "Output" width:50 tooltip:"Output Attachments List to MAXscript Listener." across:2
button reset_list "Reset" width:50 tooltip:"Reset Attachments List."
button list_help "Help" width:110
)
on list_help pressed do
(
messagebox "Use the Attachments Utility to keep track of the order you used to attach mesh objects." title:"Attachments Utility"
)
on save_list pressed do
(
print "Attachments Utility"
for i = 1 to m_list.count do
(
gw = m_list[i]
-- Output Attachments List to the Listener
print gw
)
)
group ""
(
listbox mesh_list items:m_list height:3
)
on obj_a picked nobj do
(
group_a = nobj
obj_a.text = group_a.name
if classof group_a != Editable_Mesh then convertToMesh group_a
select group_a
)
fn make_attach nobj =
(
diffuse_a = diffuse_b = undefined
group_b = nobj
gw = group_b.name as string
gwo = group_a.name as string
if classof group_b != Editable_Mesh then convertToMesh group_b
if no_attach.checked == false then
(
attach group_a group_b
attach_num = attach_num + 1
)
next = m_list.count + 1
m_list[next] = next as string+": "+gwo + "->" + gw
mesh_list.items = m_list
select group_a
)
on obj_b picked nobj do
(
make_attach nobj
)
on reset_list pressed do
(
group_a = group_b = undefined
m_list = #()
attach_num = 0
mesh_list.items = m_list
obj_a.text = "Source"
)
) -- rollout
-- This fn works to glue mesh pieces together.
rollout glue_roll "Surf Glue"
(
local pick_str, objA, objB, index, arrA = #(), arrB = #()
group "Source Data"
(
pickbutton p_a "OBJECT A" width:80
pickbutton p_b "OBJECT B" width:80
button store "COPY" width:80 tooltip:"Stores the overlaps to memory"
spinner set_thres "Scale:" range:[0, 1, .1] scale:.1 type:#float width:60 align:#center
label p_ab "A<-->B" across:2
label p_count "[Unknown]" align:#left
)
listbox glue_items "Pieces:" items:glue_names height:3
button paste "GLUE" width:50 tooltip:"GLUE together mesh pieces." across:2
button reset "Reset" width:50 tooltip:"Reset this tool."
button r_obj "Restore Object" width:110 tooltip:"Restore OBJECT B"
button g_help "Help" width:110
on g_help pressed do
(
messagebox "Use this utility to close the gaps between detached mesh pieces." title:"GLUE"
)
-- This feature will allow users to examine a Vertex Selection when they click
-- on the group.
on glue_items selected val do
(
try (
setvertselection glue_objA[val] glue_pos[val]
setvertselection glue_objB[val] glue_set[val]
update glue_objA[val]
update glue_objB[val]
) catch (
messagebox "You cannot select a deleted scene node." title:"GLUE"
)
)
on r_obj pressed do
(
try (
f = getnumverts objB
) catch (
messagebox "You cannot restore a deleted scene node." title:"GLUE"
return 0
)
count = 1
g_index = glue_items.selection
for i in glue_set[g_index] do
(
setvert objB i previous_b[count]
count = count + 1
)
update objB
)
on reset pressed do
(
glue_set = #()
glue_names = #()
glue_items.items = glue_names
p_a.text = "OBJECT A"
p_b.text = "OBJECT B"
p_count.text = "[Unknown]"
objA = objB = undefined
previous_b = #()
)
on p_a picked nobj do
(
objA = nobj
p_a.text = objA.name
select objA
progressStart "Transforming Data....."
type = classof objA
if type == editable_mesh or type == sphere or type == box then
(
converttomesh objA
numV = getnumverts objA
arrA[1] = #()
for i = 1 to numV do
(
calcy = (i as float / numV as float) * 100
calcy = calcy as integer
progressUpdate calcy
arrA[1][i] = getvert objA i
)
)
progressEnd()
)
on p_b picked nobj do
(
objB = nobj
p_b.text = objB.name
select objB
progressStart "Transforming Data....."
type = classof objB
if type == editable_mesh or type == sphere or type == box then
(
converttomesh objB
numV = getnumverts objB
arrB[1] = #()
for i = 1 to numV do
(
calcy = (i as float / numV as float) * 100
calcy = calcy as integer
progressUpdate calcy
arrB[1][i] = getvert objB i
)
)
progressEnd()
)
-- This function saves the colliding vertices from obj B to an array.
fn copySel objA objB thres =
(
local pntA, pntB, diff
a = arrA[1].count
b = arrB[1].count
g_index = glue_set.count + 1
arry = #()
count = 0
glue_objB[g_index] = objB
glue_objA[g_index] = objA
glue_pos[g_index] = #()
glue_set[g_index] = #()
progressStart "Searching...."
-- Look for a collision
for i = 1 to a do
(
pntA = arrA[1][i]
prcnt = (i as float/a as float * 100.0) as integer
progressUpdate prcnt
loc = findItem arrB[1] pntA
if loc == 0 then
(
for j = 1 to arrB.count do
(
pntB = arrB[1][j]
diff = process_diff pntA pntB
if diff < thres then
(
loc = j
exit
)
) -- loop
) -- if
if diff < thres or loc != 0 then
(
count = count + 1
append glue_set[g_index] loc
append glue_pos[g_index] i
append arry i
)
) -- loop i
progressEnd()
convertTomesh objB
setvertselection objB glue_set[g_index]
convertToMesh objA
setvertselection objA arry
gw = objA.name as string + "<-->" + objB.name as string
if count > 1 then
(
glue_names[g_index] = gw
try (
glue_items.items = glue_names
) catch (
messagebox "Unknown Error"
return 0
)
gw = "["+count as string+"]"
p_count.text = gw
)
count
)
on store pressed do
(
if classof objA == Editable_mesh and classof objB == Editable_mesh then copySel objA objB set_thres.value
)
fn pasteSel =
(
index = glue_items.selection
count = 1
-- ReSelect the objects. Update the data.
try (
select glue_objA[index]
a = selection as array
objA = a[1]
) catch (
messagebox "Please click COPY first to copy the vertex positions before performing a GLUE operation." title:"GLUE"
return 0
)
try (
select glue_objB[index]
a = selection as array
objB = a[1]
) catch (
messagebox "Please click COPY first to copy the vertex positions before performing a GLUE operation." title:"GLUE"
return 0
)
previous_b = #()
try (
for i in glue_set[index] do
(
append previous_b (getvert objB i)
pnt = getvert objA glue_pos[index][count]
setvert objB i pnt
count = count + 1
)
update objB
) catch (
messagebox "You have made an invalid selection. This node has been deleted." title:"GLUE"
)
)
on paste pressed do
(
pasteSel()
)
) -- rollout
Just dounloaded and installed Textools. When I try to unwrap with a help of Roadkill nothing happens. Nothing at all. I set the path to Roadkill correctly. Am I missing anything else?
I would love to add textools to 2012, it's nice that autodesk upped their game but I like the flow of the tools in textools. I don't think it would be bad to have a choice.
Just dounloaded and installed Textools. When I try to unwrap with a help of Roadkill nothing happens. Nothing at all. I set the path to Roadkill correctly. Am I missing anything else?
I second this. My roadkill exe isn't named "roadkill.exe" so maybe that's the problem, I didn't rename it, and I wouldn't figure that name would be hardcoded like that, I can't get it to work either though. Still been just exporting and reimporting. Which is messy of course.
I'm currently using tex tools in max 2012. Which is awesome Autodesk might have updated there tools but you still have a whole set that they still don't have. I am currently having issues with the ambient occlusion and the light tracer but other than that the rest of the tool are great. I wanted to know if there was a way to dock the tool on to a side panel or a panel on the bottom. It might not bother some people but I would like to have the tool docked just so that it's more a part of the interface and jut not floating around. Thanks again man that is an awesome tool!
Hi all! sorry for my English I have scene with more then 400 objects, which i need to pack at 5 textures with 2048x2048 dimensions. Where and what objects will be placed on current texture are no so important. My problem is that the pack method from the textools don't consider from aspect ratio of the texture correctly. All works fine if we have aspect ratio = 1. For example, set in the textools toolbar height 256 and width 1280 (256*5 textures), then select objects and open UV editor. We see that all objects are stretched by 5 times in horizontal (only if we were mapped objects when aspect ratio was set to 1 by default). Set horizontal scale to 20% (100/5) to parry distortion and use the pack method. And we see that average offsets between objects by horizontal and vertical are not equal. Result horizontal offset will be more than vertical offset by 5 times.
Can i fix it? If it is, where i can see the pack function?
I'm sure that i'm just doing something dumb here as i'm not really a max guy, but i'm trying to get the uv island -> smoothing group function to work. Basically nothing happens when I click the "split" button. I've tried being in various modes, 3d, uv, polygon in edit mesh etc etc.
The Split button takes your smoothing groups and sets your uv seams at the hard edges.
The UV-Island->Smoothing Group is in the little 'Tools' rollout above the Edit UV button. Are you using the "Smoothing groups from UV Shells" under this toolset?
always make sure you have a editable poly as a base at least because meshes are not everywhere supported in TexTools & I don't care about them. I haven't look yet into it but I assume that it translates the first UV channel to the smoothing groups so make sure the UV channel you want is the 1st.
Pardon me for sounding like a dork, but for some reason once I installed tex tools very few of the tools actually work. The Linear, Relax, and Checkered/Shaded buttons work, but the ones which I really need (like Mirror, Stitch and Align) don't. I am using a Student version of Max 2011, which I'm pretty sure I read Tex Tools should work with.
always operate on editable poly objects, not meshes. 90% of the tools are written soley for the editable poly object type as the mesh object is outdated. I haven't tested myself TexTools with max 2011 (2010 here at work) but I highly assume that it is compatible.
It looks like that's the problem, thank you for your help!
I guess my problem now is that I've already rigged my model. If I add the Editable Poly modifier wont that screw up my weights and stuff?
use skin tools in utilities to extract skin weights, then you can change the mesh to edit poly, re-apply skin, import the weights back from the skindata and you should be sorted. Good luck!
just in general: its always good to first do the modeling and unwrapping (they belong together as they define the mesh or poly data) and then apply additional modifiers such as the skin modifier.
Some functions (texel ratio related) do expect however the base object to be a editable poly with the same tris and face count as the latest state of the modifier stack- its things like these that make it easier to make scripts for apps such as modo.
Big thanks in the first place for this great tool!
and just to share a bit of my findings using non-programmer skills... i wanted to keep polygons or quads on the mesh after its been unwrapped with roadkill ( thus exported to obj and imported back ), rather than have the messy triangles (under max 2009)
this script "fn_40__unwrap_with_roadkill.ms" changes the OBJ export settings to use triangles.
so to use quads you need to change the 13th value in this line from 0 to 1
to use polygons you need to change the 13th value in this line to 0 to 2
hm, it wasnt such a good idea after all to change to quads for exporting OBJ
roadkill gives very different results for some objects with often unusable results
so triangles it is.
...care to create textools for SI??? I am so missing this tool in SI!!!! ...anyways just wanted to say thanks for this awesome tool, although I cant use it at work anymore!
Hi renderhjs , I can't thank You enough for TexTools, been using it for three years now, I think
a very little thing , the get/set texel density works only for UV channel 1,regardless of what UV channel I set to be edited
It seems I can't use the normalize function on channel 2. It always use the channel 1. Is this the wanted behavior ?
It's a bit annoying as I would particularly like this option to normalize my uv's for a lightmap packing. For now i have to switch my channels which is lil tedious.
Thanks for helping.
Hey Render, I was also wondering if there's plans to make more of the tools work while editing multiple objects? Things like normalizing the texel ratio for all islands, or aligning an island via an edge selection, don't work with multiple objects selected.
Regardless, great toolset and I've been using it since it's initial release. It's most welcomed
I was wondering how much 3ds max 2012 has improved in the UV tools, and how much this plugin is still needed, is there still loads of features missin in 2012?
Regarding Uvs TexTools offers only few features max is missing (nothing big though). Its worth using Textools for its map generation features. It handles copying texel density better. Autodesk after few years almost caught up to renderhjs...almost ;p
couple of functions I still keep textools installed for:
- aligning shells to each other
- aligning a single shell to the edge you've selected (rotates the shell so that edge is straight). very cool.
- it's one-click relax button always gives me cooler results
- uv shell to smoothing group
- and yeah, various masks
More like took his ideas and made them not as good.
Yeaaaaaaahhh... true dat ;p For example normalizing uv shells in 3ds max pretty much... doesn't work. Yeah it kinda works but TexTools uses math to do this... 3ds max guesses...at least thats what the result looks like.
I've found the normalizing in Max to work pretty well, actually. Seems accurate, at least. If you get time, some comparison shots would be interesting.
Just a little question.. I just come back to max, first reflex.. TEXTOOLS :P A lot of improvement, seem's cool but after instaling ( files are in the proprer folder, auto instal worked great) the launch bouton doesn't work : / I had no problem with older version and max 2009, but i currently use 2012 64bit now (fresh install & admin right).. Any idea? 2012 is not listed in supported version, but i knew it works properly with max 2012..
Small question.
I'm wondering if there are any plans to let the Texel Density be measured in Pixels/WorldUnit instead of Pixels/MaxUnit - I measure things in meters and it is a hassle to constantly do the math to keep the proper proportions of things.
Just a little question.. I just come back to max, first reflex.. TEXTOOLS :P A lot of improvement, seem's cool but after instaling ( files are in the proprer folder, auto instal worked great) the launch bouton doesn't work : / I had no problem with older version and max 2009, but i currently use 2012 64bit now (fresh install & admin right).. Any idea? 2012 is not listed in supported version, but i knew it works properly with max 2012..
Any suggestion?
I don't have a copy of max 2011,2012 or greater available to me. These days I use texTools at work for unwrapping and texturing but I don't actively develop it anymore because it does what I needed it to do within 3dsmax 2010 as its the 3dsMax version my company works with.
But I also stated hopefully clear enough in the past that the MZP textools file is just a ZIP so you can open it and explore the scripts, use them for other scripts and or improve them with the new 3dsMax 2012 or greater versions.
I got an email back in 2011 I think from Autodesk that they would like to take some ideas and tools from TexTools into 3dsMax and I believe my credits should be within 3dsMax 2012 because of the UV tools. For me though that was a sign that they finally addressed the UV tools and there is not much need for me trying to fix max with TexTools.
Small question.
I'm wondering if there are any plans to let the Texel Density be measured in Pixels/WorldUnit instead of Pixels/MaxUnit - I measure things in meters and it is a hassle to constantly do the math to keep the proper proportions of things.
Shouldn't be to hard to open the script and look into it yourself. I know that maxscript has some convertion tools for the units. Personally I work closer with Game Engines that I write or modify myself, which is why real units (internal units) matter as the real deal. But for some people that use UDK, Unity etc. and that prefer unit conversions (unity scales e.g by default units to 0.01).
If you are not that great or skilled in Maxscript, try to find at least the script within the TexTools.mzp (zip file), make a thread here in Tech talk on polycount and ask people to help you with it so works with set units in Max.
Replies
Basically I normalize the shells, then use the pixel ratio picker, but it does give me this error:
pix2: -1.#IND
It was working ok but then I am not sure what I did and started getting that! Thanks for your help! I'll appreciate it!
i tried the manual install, throwing errors
The edges in the render are offset, and doesn't match the UVs...also, the lowest thickness (1) is way too fat, and my model is to scale (if that matters, it's a tank)
Also can't get the linear align to work now in 2011. It never quite straightens it, always have to use the old scaling method again to get lines straight.
Still an amazing tool overall and can't live without it.
maybe you can implement something from this stuff.. I'd like to see transfer of UV from hipoly to lowpoly for example.. missing this
-- UV Layout -- This program was split off from the Surface Toolkit.... Thanks to the guy who coded the TV drawing routine I'm using. -- please email me at vajuras@planetunreal.com so I can give ya credit. www.cyberkreations.com/kreationsedge -- Updated May 9, 2001 to work in Max 4. I think my interest in this was rekindled so that it could be used to help Chilliskinner advance. -- On jan. 23 the unwrap algorithm was vastly improved to handle unwraps on the right -- and left side. Also, added Match UVW function/utility. -- 2/7 -- Made the Camera Projection Logic. -- 2/14 -- Improved "Update Map" routine which would have an error. -- Render_map will now convert object to mesh if not an Editable Object. -- Added ability to separately render TVFaces without splitting a mesh object. This -- method seems to work much better and return more accurate results... -- 2/20 -- Update_Texture now works with Multi-materials... -- 2/23 -- started working on the Skin Composer routine. -- 3/02 -- Actually put in the AI for Blender. -- Upgraded the UV Manager to create an alpha channel for uv maps. -- Blender uses advanced algorithm to compute across any range [n, n] instead of [0, 100]. -- This is the code that does the math: -- dist = abs(sort_gy - sort_ly) -- pnt = 1/dist -- p_dist = abs(pix.y - sort_ly) -- blend_pnt = p_dist*pnt -- 3/12 -- Added Attachments Utility -- 4/17 -- Fixed a bug where the VertexSelection did not work for SurfUnwrap. -- v1.2 -- 4-21 -- Added Edge Colors & Surface Glue. -- Extended the range of the size of an image. -- The Script used to crash if the restore 0r Apply under Project UVW button was pressed. -- You can now enter float values for the Width & Length of a bitmap (UV Manager) -- A lot of easy crashes were fixed that may be caused if a user clicks the wrong button. -- v1.2.2 -- Fixed a crash that may occur during a 'render map' when the color comes up undefined... -- Default Material checkbox will now go & load the Material's diffusemap. Changes a BitmapTexture to Bitmap. -- Increased User Feedback -- v1.2.3 -> v1.2.4 -- Fixed the bug in which the edge color array would crash the utility. -- The utility displays the expiration date. -- v1.3 -- Added UV Mesher Tool. -- Improved Glue Tool Performance. -- v1.3.3 -- UV Mesher grabs the dimension of the Diffuse Channel Bitmap if available. -- WARP feature for the UV Mesher was fixed. -- v1.4 -- UpdateMap feature now reloads all the mapping channels instead of just the diffuse channel. utility surfHelp "UV Layout" ( local sort_y, morph_state = false -- stores the selection of cross objects local b_cross = #() local b_crossNum = 0 local s_width = 190 local s_height = 345 -- For the 3D Paint Options local default_secs = 3600 local map_type = 0 --default mapping: planar local uvw_axis = 0 -- Emergency ShutDown local shutDown = false -- Determines if this script is registed, if so, all features are enabled. local register = true label display_global "" --stores color for each edge (UV Layout & Edge) local color_mat = #() -- global user_color local global_color = red -- selection set data local sel_set =#(), sel_index = 1 -- Variables for GLUE local glue_set = #() local glue_names = #() local glue_objA = #() local glue_objB = #() local glue_pos = #(#()) local previous_b = #() -- Stores the original OBJECT B -- Variables for UV Mesher local face_set = #() -- Stores the origin for the TVFace. fn grab_sel = ( selection as array ) -- Selects all the faces in a mesh. fn setFaceArray tar numF = ( tar = #() for i = 1 to numF do ( tar[i] = i ) return tar ) -- Retrieves the date from a string and returns it in a 3-element array. The string must conform to localTime String Conventions -- meaning a space character must be included like "11/23/98 8:23PM". This function can also tokenize a regular string. -- This routine automatically looks for / and \\ (backslash) but, an extra delimeter can be searched. -- By default, you should send a space character. fn get_all_tokens str_in delimeter = ( tokens = #() str = str_in as string len = str.count count = 1 -- Count the # of tokens elmo = 0 -- Count valid charcters for a = 1 to len do ( if str[a] == "/" or str[a] == delimeter or str[a] == "\\" then ( this_token = substring str count elmo append tokens this_token -- Set the pointer to the beginning of the next slot. count = a + 1 elmo = 0 ) else ( -- Count the valid characters elmo = elmo + 1 ) ) if shutDown == true then ( shutDown == true return #() ) -- Return the last token also. this_token = substring str count elmo append tokens this_token tokens ) -- Replace all occurances of a character with another. fn replace_char str delimeter new_char = ( len = str.count for a = 1 to len do ( if str[a] == delimeter then str[a] = new_char ) str ) -- Opt 1: Encrypt -- 2: Not Encrypted fn read_data file_name opt = ( count = 1 j_name = #() if opt == 2 then file = openFile (file_name) else file = openEncryptedFile (file_name) 252525 if file != undefined then ( while (not eof file) do ( str = readline file j_name[count] = str count = count + 1 ) close file ) -- if j_name ) -- Opt 1: encrypt -- 2: not encrypt fn save_data file_name path_string opt = ( if file_name != undefined then ( if opt == 1 then file = createFile(file_name+".tmp") else file = createFile(file_name) format "%\n" path_string to:file close file ) -- if if opt == 1 then ( encryptFile (file_name+".tmp") file_name 299525 deleteFile (file_name+".tmp") ) ) -- Use the .ini file to determine where the MAX ROOT is. fn get_root = ( f = read_data "uv_max/layout.ini" 2 if f.count < 1 then ( messagebox "Please press the Online Help (.chm) button first to install the .ini file before continuing." return #() ) tokens = get_all_tokens f[1] "-" tokens ) -- warez -- This function will return another point that lies in the same spline. This func is called by the unwrap_spline -- func which in turn uses this AI to help determine which xAxis to assign to a spline knot. The Spline Ends Algorithm -- is applied which states that the very next vertex in the spline order can be used to determine the direction of the -- knot. fn findOtherKnot obj spl vert = ( local pnt numK = numKnots obj spl old_pnt = getKnotPoint obj spl vert next_vert = vert - 1 if vert >1 then next_vert = vert - 1 else next_vert = vert + 1 pnt = getKnotPoint obj spl next_vert gw = "NEXT:"+next_vert as string+" SENT:"+vert as string -- Return the same point back if no vert exists on a different xAxis in the same spline. pnt ) -- This function will return another point that lies in the same spline. This func is called by the radialSolver -- func which in turn uses this AI to help determine which xAxis to assign to a spline knot. fn simpleFindOtherKnot obj spl vert = ( local pnt numK = numKnots obj spl old_pnt = getKnotPoint obj spl vert for i = 1 to numK do ( pnt = getKnotPoint obj spl i if old_pnt.x != pnt.x then return pnt ) -- Return the same point back if no vert exists on a different xAxis in the same spline. pnt ) -- This function makes any knot that equal the deformed knot move also similar to the Global Transform Func. -- Since the new position is along the yAxis, it will not be deformed again. -- Var: -- new_pos - new position where this vert should move fn makeAllKnotsEqual obj spl vert new_pos thres = ( nums = numSplines obj old = getKnotPoint obj spl vert -- current position of the before the new move. for i = 1 to nums do ( numk = numKnots obj i for j = 1 to numk do ( pnt = getKnotPoint obj i j -- Exclude the spline where the original knot came from. Spline Ends Algorithm. pntxUp = old.x + thres pntxDown = old.x - thres pntyUp = old.y + thres pntyDown = old.y - thres pntzUp = old.z + thres pntzDown = old.z - thres if i != spl and (pnt.x <= pntxUp and pnt.x >= pntxDown) and (pnt.y <= pntyUp and pnt.y >= pntyDown) and (pnt.z <= pntzUp and pnt.z >= pntzDown) then ( pnt = new_pos setKnotPoint obj spl vert pnt ) ) ) ) -- This function will solve for a radial b-spline since the Spline Ends Algor. fails if the points lie within the -- threshold. fn radialSolver obj spl vert = ( numS = numSplines obj new_spline = spl - 1 -- limit checks if new_spline < 1 then new_spline = spl + 1 if new_spline > numS then new_spline = spl pnt = simpleFindOtherKnot obj new_spline vert -- Return another point on another spline that can be used to determine the direction. pnt ) -- This function unwraps a spline object. If the surf is cylindrical/spherical, the knots along the mid spline behind the base axis -- will be pulled apart. Spline Ends Algorithm examines a spline and will assign to the middle knot automatically the direction -- the knot should go which is naturally, determined by the Order of the spline. -- b_a - Y axis -- x_a - X axis fn unwrap_spline obj b_a x_a thres output = ( local num_s, num_k, val, pnt, gw dist = 0.0 -- distance pnt from x_a try ( num_s = numSplines obj ) catch ( messagebox "Please choose a valid object." return 0 ) for i = 1 to num_s do ( num_k = numKnots obj i calcy = (i as float / num_s as float) * 100 calcy = calcy as integer lc = calcy as string + "%" output.text = lc for j = 1 to num_k do ( pnt = getKnotPoint obj i j gw = i as string + ":" + j as string + " "+pnt as string -- If the point is below the base axis, adjust the val variable. if pnt.y > b_a then val = pnt.y - b_a else val = 0 ny = pnt.y nx = pnt.x dist = (abs(x_a - pnt.x))/100 -- New Y coordinate will be at base axis. If Y coordinate is less than base axis, then let it keep its position. if pnt.y > b_a then ( ny = b_a --nx = pnt.x - val --gw = "Behind base "+i as string+":"+j as string+" "+pnt.x as string --print gw -- If the point is along the middle spline behind the base axis.... Determine which direction to move. midUp = (x_a as float + thres as float) midDown = (x_a as float - thres as float) if pnt.x <= midUp and pnt.x >= midDown then ( otherKnot = findOtherKnot obj i j --gw = "Mid "+i as string+":"+j as string --print gw -- If the Spline Ends Algorithm has failed in case of radial b-splines then, use the radial b-solver algorithm -- instead. if otherKnot.x == pnt.x then otherKnot = radialSolver obj i j est = otherKnot.x if est < (x_a) then nx = pnt.x - (val * dist) else nx = pnt.x + (val * dist) -- Now, deform any other colliding vertices.... The deformed verts will not ever be deformed again because -- they will equal the b_a. new_pnt = [nx, ny, pnt.z] makeAllKnotsEqual obj i j new_pnt thres ) else ( if pnt.x < x_a then nx = pnt.x - (val * dist) else ( if pnt.x > x_a then ( nx = pnt.x + (val * dist) gw = gw + "RIGHT " new_pnt = [nx, ny, pnt.z] --makeAllKnotsEqual obj i j new_pnt thres ) -- if ) -- if ) -- if ) -- if setKnotPoint obj i j [nx, ny, pnt.z] updateShape obj --gw = gw + " "+nx as string + ":" + ny as string + " " + val as string --print gw ) ) try ( ) catch ( messagebox "Please select a valid object" return 0 ) output.text = "->" ) -- This is a general func designed to work with any type of surface. It -- takes the object and computes the highest or lowest x value. -- option 1: largest, opt 2: least fn compute_percent obj opt = ( local sort = 0, diff_x = 0.0, sort_x = 0 convertToMesh obj numS = getnumverts obj for i = 1 to numS do ( pnt = getvert obj i if i == 1 then ( diff_x = pnt.x sort_x = i ) else ( if opt == 1 and pnt.x > diff_X then ( sort_x = i diff_x = pnt.x ) if opt != 1 and pnt.x < diff_X then ( sort_x = i diff_x = pnt.x ) ) ) sort_x ) -- This function unwraps a mesh object. The val is computed to determine the total -- distance a point should be moved along the X axis. The dist is computed as the -- distance the point is from the X axis which gives a percentage value. -- b_a - Y axis -- x_a - X axis fn unwrap_mesh obj b_a x_a x_width use_stack disp = ( local num_s, num_k, val, pnt, gw, target = #(), i, sort_x dist = 0.0 -- distance pnt.x is from x_a divided by 100 to get -- a percentage. This computation will then be used -- to determine how far from the X axis the points -- should be deformed. count = 1 select_i = 0 -- turns on if stack is present... num_verts = 0 try ( dummy = getnumfaces obj ) catch ( convertToMesh obj ) --Had to take out unwrap support for a vertex selection. It was a useless feature anyway... num_verts = getnumverts obj if select_i == 0 or use_stack == false then ( for i = 1 to num_verts do ( target[i] = i ) ) -- if disp.text = "Analyzing Topology...." sort_x = compute_percent obj 1 far_vert = getvert obj sort_x sort_neg = compute_percent obj 2 least_vert = getvert obj sort_neg -- Get the percentage from the farthest vert. Then, this value will be used -- to equate the values to a range [low, 100] percent = 100 / (abs(x_a - far_vert.x)) percent_neg = 100 / (abs(x_a - least_vert.x)) progressStart "Unwrapping..." cond = true count = 1 for i in target do ( calcy = (i as float / num_verts as float) * 100 calcy = calcy as integer lc = calcy as string + "%" if select_i == 1 then disp.text = "Using Stack Selection...." else cond = progressUpdate calcy if cond == false then return 0 try ( pnt = getvert obj i ) catch ( pnt = getvert obj count ) -- If the point is below the base axis, adjust the val variable. if pnt.y > b_a then val = pnt.y - b_a else val = 0 ny = pnt.y nx = pnt.x if pnt.x < x_a then dist = (abs(pnt.x - x_a)) * percent_neg else if pnt.x > x_a then dist = (abs( pnt.x - x_a)) * percent dist = dist / 100 -- If the user wants to morph the mesh, then let dist always eq. 1 so -- that the verts may be deformed. --if morph_state == true then dist = 1 -- New Y coordinate will be at base axis. If Y coordinate is less than base axis, then let it keep its position. if pnt.y > b_a then ( ny = b_a --nx = pnt.x - val -- Include the middle spline as well -- If midpoint is from LEFT TO RIGHT then mid = x_a + 1 --mid = x_a + 1 if pnt.x < x_a then nx = pnt.x - (val * dist) else ( if pnt.x > x_a then ( nx = pnt.x + (val * dist) ) total = val * dist --gw = "Dist:"+dist as string+" Val:"+val as string+" Total:"+total as string --print gw ) ) -- if try ( setvert obj i [nx, ny, pnt.z] ) catch ( setvert obj count [nx, ny, pnt.z] ) --gw = gw + " X:Y "+nx as string + ":" + ny as string + " V:" + val as string --print gw count = count + 1 ) update obj disp.text = "" progressEnd() -- Return target array. target ) -- This function uses the array (spl) to restore the original target. fn restore_spline obj spl output = ( local token = #() local count = 1 if classof obj != editable_mesh then return try ( lengt = spl.count for i = 1 to lengt do ( calcy = (i as float / lengt as float) * 100 calcy = calcy as integer lc = calcy as string + "%" output.text = lc token = get_token spl[i] " " setKnotPoint obj (token[1] as Number) (token[2] as Number) [token[3] as float, token[4] as float, token[5] as float] ) ) catch ( messagebox "Cannot restore shape." return 0 ) output.text = "->" updateShape obj ) fn restore_mesh obj target spl output = ( leng = target.count -- Get the Vertex Selection real_num = getnumverts obj -- Get # of verts in the mesh object count = 0 collapseStack obj if leng < 1 then ( -- Fill target array for i = 1 to real_num do ( count = count + 1 target[count] = getvert obj i ) ) count = 0 progressStart "Restoring..." for i in target do ( count = count + 1 calcy = (count as float / leng as float) * 100 calcy = calcy as integer progressUpdate calcy -- An empty target array means No Vertex Selection was made. if leng == real_num or leng < 1 then setvert obj count spl[count] else setvert obj i spl[i] --gw = "Restore:"+spl[count] as string ) try ( --output.text = "->" progressEnd() ) catch () update obj ) -- This func draws the pixels in-between the 2 points. fn draw_line x1 y1 x2 y2 uvBMP u_color = ( pnt = #() -- Compute the pixels between 2 points. First, derive the distance. dist_x = abs(x2 - x1) dist_y = abs(y2 - y1) if x1 > x2 then l_x = x2 else l_x = x1 if y1 > y2 then l_y = y2 else l_y = y1 -- Let i take the value of y for i = 1 to (dist_y) do ( -- move right diff_x = x2 - x1 diff_y = y2 - y1 pnt[i] = [0,0] if diff_x > 0 then pnt[i].x = diff_x if diff_y > 0 then pnt[i].y = diff_y ) ) -- Bresenham line implimentation for MAXScript -- by Harry Denholm, Kinetix 1998 function BLine bmp x1 y1 x2 y2 c old_color = ( -- stick the colour into an array local ary = c if ary == undefined then ary = #(old_color) -- assign originals local Xb = x1 as integer local Yb = y1 as integer -- build the line deltas local dX = x2-x1 as float local dY = y2-y1 as float -- straight horiz if dy == 0.0f do ( local xsign = 1 if xb > x2 then xsign = 1 as integer if x2 < xb then xsign = -1 as integer setPixels bmp [xb,yb] ary while xb != x2 do ( xb += xsign setPixels bmp [xb,yb] ary ) return true ) -- straight vertical if dx == 0.0f do ( local ysign = 1 if yb > y2 then ysign = 1 as integer if y2 < yb then ysign = -1 as integer setPixels bmp [xb,yb] ary while yb != y2 do ( yb += ysign setPixels bmp [xb,yb] ary ) return true ) -- no straights, go for bresenham line slide -- set up the movements local xsign = 1 if xb > x2 then xsign = 1 as integer if x2 < xb then xsign = -1 as integer local ysign = 1 if yb > y2 then ysign = 1 as integer if y2 < yb then ysign = -1 as integer dx = abs(dx) dy = abs(dy) setPixels bmp [xb,yb] ary -- line more vertical than horizontal if dx < dy then ( p = 2 * dx - dy const1 = 2 * dx const2 = 2 * (dx - dy) while yb != y2 do ( yb += ysign if p < 0 then ( p = p + const1 ) else ( p += const2 xb += xsign ) setPixels bmp [xb,yb] ary ) ) -- line more horizontal than vertical else ( p = 2 * dy - dx const2 = 2 * (dy - dx) const1 = 2 * dy while xb != x2 do ( xb = xb + xsign if p < 0 then ( p = p + const1 ) else ( p = p + const2 yb = yb + ysign ) setPixels bmp [xb,yb] ary ) ) ) -- Set material color to the bitmap. fn setMaterial uvBMP inMat = ( iW = uvBMP.width iH = uvBMP.height inMat.alpha = 0.0 mat_color = #(inMat) for i = 1 to iW do ( for j = 1 to iH do ( setPixels uvBMP [i,j] mat_color ) ) ) -- This function renders the unwrapped surface or face selection. -- opt - use face selection from stack. fn render_uvw obj uvBMP u_color opt = ( target = #() try ( iW = uvBMP.width iH = uvBMP.height ) catch ( messagebox "Please select a bitmap or uncheck the Include Bitmap option." return 0 ) status = false count = 0 edge_num = 0 t_count = getnumfaces obj Uobj = obj cCol = newCol = u_color display_global.text = "Rendering UVW Coordinates..." progressStart "Rendering..." -- Always assume that a UVWmap mod is at the top so, grab the next mod. try ( target = getFaceSelection Uobj Uobj.modifiers[1] ) catch ( try ( target = getFaceSelection Uobj Uobj.modifiers[2] ) catch ( -- Set the func. to use all the faces in the mesh. opt = false ) -- catch ) -- catch gw = "Faces:"+t_count as string+" "+target as string print gw if opt == false then ( target = #() --delta = (100.0/(getnumfaces Uobj)) as float for a = 1 to t_count do ( target[a] = a ) ) limit = target.count for t in target do ( -- update our progress bar count = count + 1 calcy = (count as float / limit as float) * 100 calcy = calcy as integer progressUpdate calcy --progressUpdate ((t*delta) as integer) status = getProgressCancel() if shutDown == true then( status == true ) if status == true then ( progressEnd() display_global.text = "" return false ) -- get the faces faceN = getface Uobj t faceT = getTVFace Uobj t -- take the TVs out of the face index tva = gettvert Uobj faceT.x tvb = gettvert Uobj faceT.y tvc = gettvert Uobj faceT.z -- unwrap TVs. If a new Edge Color exists, use that color instead. try ( edge_num = t * 3 newCol = #(color_mat[edge_num-2]) if color_mat[edge_num-2] == 0 then newCol = cCol ) catch ( newCol = cCol ) if newCol == undefined then newCol = cCol if (getedgevis Uobj t 1 and color_mat[edge_num-2] != -1) then \ Bline uvBMP (iW*tva.x) (iH*(1.0-tva.y)) (iW*tvb.x) (iH*(1.0-tvb.y)) newCol cCol try ( edge_num = t * 3 newCol = #(color_mat[edge_num-1]) if color_mat[edge_num-1] == 0 then newCol = cCol ) catch ( newCol =cCol ) if newCol == undefined then newCol = cCol if (getedgevis Uobj t 2 and color_mat[edge_num-1] != -1) then \ Bline uvBMP (iW*tvb.x) (iH*(1.0-tvb.y)) (iW*tvc.x) (iH*(1.0-tvc.y)) newCol cCol try ( edge_num = t * 3 newCol = #(color_mat[edge_num]) if color_mat[edge_num] == 0 then newCol = cCol ) catch ( newCol =cCol ) if newCol == undefined then newCol = cCol if (getedgevis Uobj t 3 and color_mat[edge_num] != -1) then \ Bline uvBMP (iW*tvc.x) (iH*(1.0-tvc.y)) (iW*tva.x) (iH*(1.0-tva.y)) newCol cCol ) progressEnd() display_global.text = "" display uvBMP ) -- This func copies the UVW coordinates and material assignments from one object -- to another. fn match_uvw target source deform_uv = ( tmap = true -- texture mapping applied t_equal = true -- if tverts equal then deform the texure vertices. If not, -- simply assign the materials. --convertToMesh target --convertToMesh source try ( a = getnumfaces target b = getnumfaces source ) catch ( return 0 ) if a != b then ( messagebox "These objects have different face counts (topology)." return 0 ) try ( c = getnumtverts target d = getnumtverts source ) catch ( --messagebox "No Mapping Applied..." --return 0 tmap = false c = d = 0 ) if c < 1 or d < 1 then ( --messagebox "No Mapping Applied..." --return 0 tmap = false ) if c != d then ( --messagebox "The objects have different counts of texture vertices." --return 0 t_equal = false ) progressStart "Setting TVFaces...." --convertToMesh source -- copy materials f = target.material source.material = f for i = 1 to a do ( calcy = (a as float / i as float) * 100 calcy = calcy as integer progressUpdate calcy -- Copy separate face assignments if Multi-Object has been applied. f = getfacematid target i setfacematid source i f if tmap == true and t_equal == true and deform_tv == true then ( f = getTVFace target i t1 = gettvert target f.x settvert source f.x t1 t2 = gettvert target f.y settvert source f.y t2 t3 = gettvert target f.z settvert source f.z t3 setTVFace source i f.x f.y f.z ) ) progressEnd() update source ) -- Update Map -- Updates a standard and multi-material's diffusemap. fn update_texture obj junk_bitmap = ( local file_name count = 0 -- count # of updated textures -- Grab the Current Selection to refresh the buffer. obj_array = grab_sel() obj = obj_array[1] try ( classify = classof obj.material ) catch ( messagebox "Please select the object first!" return false ) if classify != Standardmaterial and classify != Multimaterial then ( messagebox "Wrong Material Type! This utility only works with\n Standard & MultiMaterials." return false ) if classify == Standardmaterial then ( try ( file_name = obj.material.diffusemap.filename obj.material.diffusemap.filename = file_name count = count + 1 update obj freeSceneBitmaps() ) catch () try ( file_name = obj.material.bumpmap.filename obj.material.bumpmap.filename = file_name count = count + 1 update obj freeSceneBitmaps() ) catch () try ( file_name = obj.material.opacitymap.filename obj.material.opacitymap.filename = file_name count = count + 1 update obj freeSceneBitmaps() ) catch () try ( file_name = obj.material.filtermap.filename obj.material.filtermap.filename = file_name count = count + 1 update obj freeSceneBitmaps() ) catch () try ( file_name = obj.material.reflectionmap.filename obj.material.reflectionmap.filename = file_name count = count + 1 update obj freeSceneBitmaps() ) catch () try ( file_name = obj.material.refractionmap.filename obj.material.refractionmap.filename = file_name count = count + 1 update obj freeSceneBitmaps() ) catch () try ( file_name = obj.material.selfillummap.filename obj.material.selfillummap.filename = file_name count = count + 1 update obj freeSceneBitmaps() ) catch () try ( file_name = obj.material.shinestrengthmap.filename obj.material.shinestrengthmap.filename = file_name count = count + 1 update obj freeSceneBitmaps() ) catch () try ( file_name = obj.material.shininessmap.filename obj.material.shininessmap.filename = file_name count = count + 1 update obj freeSceneBitmaps() ) catch () try ( file_name = obj.material.specularmap.filename obj.material.specularmap.filename = file_name count = count + 1 update obj freeSceneBitmaps() ) catch () try ( file_name = obj.material.ambientmap.filename obj.material.ambientmap.filename = file_name count = count + 1 update obj freeSceneBitmaps() ) catch () if count > 0 then messagebox (count as string +" Bitmap Texture(s) has been updated.") ) if classify == Multimaterial then ( -- what are the number of sub-materials? mat_count = obj.material.numsubs -- Set the loop to go through the obj.material array for a = 1 to mat_count do ( try ( file_name = obj.material[a].diffusemap.filename obj.material[a].diffusemap.filename = file_name count = count + 1 update obj freeSceneBitmaps() ) catch () try ( file_name = obj.material[a].bumpmap.filename obj[a].material.bumpmap.filename = file_name count = count + 1 update obj freeSceneBitmaps() ) catch () try ( file_name = obj.material[a].opacitymap.filename obj.material[a].opacitymap.filename = file_name count = count + 1 update obj freeSceneBitmaps() ) catch () try ( file_name = obj.material[a].filtermap.filename obj.material[a].filtermap.filename = file_name count = count + 1 update obj freeSceneBitmaps() ) catch () try ( file_name = obj.material[a].reflectionmap.filename obj.material[a].reflectionmap.filename = file_name count = count + 1 update obj freeSceneBitmaps() ) catch () try ( file_name = obj.material[a].refractionmap.filename obj.material[a].refractionmap.filename = file_name count = count + 1 update obj freeSceneBitmaps() ) catch () try ( file_name = obj.material[a].selfillummap.filename obj.material[a].selfillummap.filename = file_name count = count + 1 update obj freeSceneBitmaps() ) catch () try ( file_name = obj.material[a].shinestrengthmap.filename obj.material[a].shinestrengthmap.filename = file_name count = count + 1 update obj freeSceneBitmaps() ) catch () try ( file_name = obj.material[a].shininessmap.filename obj.material[a].shininessmap.filename = file_name count = count + 1 update obj freeSceneBitmaps() ) catch () try ( file_name = obj.material[a].specularmap.filename obj.material[a].specularmap.filename = file_name count = count + 1 update obj freeSceneBitmaps() ) catch () try ( file_name = obj.material[a].ambientmap.filename obj.material[a].ambientmap.filename = file_name count = count + 1 update obj freeSceneBitmaps() ) catch () ) -- loop if count > 0 then messagebox (count as string +" Bitmap Textures has been updated.") ) -- if true ) -- this func looks for the uvwmap mod in the modifier stack and then the modifier -- is returned. fn check_stack mod_name obj = ( local mod_n this_stack = obj.modifiers.count for i = 1 to this_stack do ( mod_n = obj.modifiers[i] names = classof mod_n if names == mod_name then exit ) return mod_n ) fn expire_script = ( messagebox "This script is time-sensitive. Please make sure your system clock is functioning properly." title:"System Clock Error" ) fn test_script = ( token = #() token = get_all_tokens localTime " " if token[3] as integer < 0 then ( print token[1] expire_script() return false ) true ) fn process_diff pntA pntB = ( diffX = abs(pntA.x - pntB.x) diffY = abs(pntA.y - pntB.y) diffZ = abs(pntA.z - pntB.z) mean = diffX + diffY + diffZ mean ) --------------------------------------------------------------------------------- -- UI --------------------------------------------------------------------------------- -- SurfUnwrap local unwrap_obj -- unwrap surface local meshU_obj -- stores the 'target' array from the vertSelection local object = true -- picked spline surf local old_spl = #() -- store the original mesh/spline local x_width = 0.0 local rPressed = 0 -- stores if restore button pressed -- Junk Bitmap local junk_bitmap = "" -- Button Width local b_width = 135, b_height = 18 rollout attach_roll "<-- Attachments -->" ( local group_a, group_b local attach_num = 0 local m_list = #() group "" ( pickbutton obj_a "Source" width:100 tooltip:"Source object" pickbutton obj_b "Attach" width:100 tooltip:"Choose mesh to attach." checkbox no_attach "Do not Attach" checked:false align:#center button save_list "Output" width:50 tooltip:"Output Attachments List to MAXscript Listener." across:2 button reset_list "Reset" width:50 tooltip:"Reset Attachments List." button list_help "Help" width:110 ) on list_help pressed do ( messagebox "Use the Attachments Utility to keep track of the order you used to attach mesh objects." title:"Attachments Utility" ) on save_list pressed do ( print "Attachments Utility" for i = 1 to m_list.count do ( gw = m_list[i] -- Output Attachments List to the Listener print gw ) ) group "" ( listbox mesh_list items:m_list height:3 ) on obj_a picked nobj do ( group_a = nobj obj_a.text = group_a.name if classof group_a != Editable_Mesh then convertToMesh group_a select group_a ) fn make_attach nobj = ( diffuse_a = diffuse_b = undefined group_b = nobj gw = group_b.name as string gwo = group_a.name as string if classof group_b != Editable_Mesh then convertToMesh group_b if no_attach.checked == false then ( attach group_a group_b attach_num = attach_num + 1 ) next = m_list.count + 1 m_list[next] = next as string+": "+gwo + "->" + gw mesh_list.items = m_list select group_a ) on obj_b picked nobj do ( make_attach nobj ) on reset_list pressed do ( group_a = group_b = undefined m_list = #() attach_num = 0 mesh_list.items = m_list obj_a.text = "Source" ) ) -- rollout -- This fn works to glue mesh pieces together. rollout glue_roll "Surf Glue" ( local pick_str, objA, objB, index, arrA = #(), arrB = #() group "Source Data" ( pickbutton p_a "OBJECT A" width:80 pickbutton p_b "OBJECT B" width:80 button store "COPY" width:80 tooltip:"Stores the overlaps to memory" spinner set_thres "Scale:" range:[0, 1, .1] scale:.1 type:#float width:60 align:#center label p_ab "A<-->B" across:2 label p_count "[Unknown]" align:#left ) listbox glue_items "Pieces:" items:glue_names height:3 button paste "GLUE" width:50 tooltip:"GLUE together mesh pieces." across:2 button reset "Reset" width:50 tooltip:"Reset this tool." button r_obj "Restore Object" width:110 tooltip:"Restore OBJECT B" button g_help "Help" width:110 on g_help pressed do ( messagebox "Use this utility to close the gaps between detached mesh pieces." title:"GLUE" ) -- This feature will allow users to examine a Vertex Selection when they click -- on the group. on glue_items selected val do ( try ( setvertselection glue_objA[val] glue_pos[val] setvertselection glue_objB[val] glue_set[val] update glue_objA[val] update glue_objB[val] ) catch ( messagebox "You cannot select a deleted scene node." title:"GLUE" ) ) on r_obj pressed do ( try ( f = getnumverts objB ) catch ( messagebox "You cannot restore a deleted scene node." title:"GLUE" return 0 ) count = 1 g_index = glue_items.selection for i in glue_set[g_index] do ( setvert objB i previous_b[count] count = count + 1 ) update objB ) on reset pressed do ( glue_set = #() glue_names = #() glue_items.items = glue_names p_a.text = "OBJECT A" p_b.text = "OBJECT B" p_count.text = "[Unknown]" objA = objB = undefined previous_b = #() ) on p_a picked nobj do ( objA = nobj p_a.text = objA.name select objA progressStart "Transforming Data....." type = classof objA if type == editable_mesh or type == sphere or type == box then ( converttomesh objA numV = getnumverts objA arrA[1] = #() for i = 1 to numV do ( calcy = (i as float / numV as float) * 100 calcy = calcy as integer progressUpdate calcy arrA[1][i] = getvert objA i ) ) progressEnd() ) on p_b picked nobj do ( objB = nobj p_b.text = objB.name select objB progressStart "Transforming Data....." type = classof objB if type == editable_mesh or type == sphere or type == box then ( converttomesh objB numV = getnumverts objB arrB[1] = #() for i = 1 to numV do ( calcy = (i as float / numV as float) * 100 calcy = calcy as integer progressUpdate calcy arrB[1][i] = getvert objB i ) ) progressEnd() ) -- This function saves the colliding vertices from obj B to an array. fn copySel objA objB thres = ( local pntA, pntB, diff a = arrA[1].count b = arrB[1].count g_index = glue_set.count + 1 arry = #() count = 0 glue_objB[g_index] = objB glue_objA[g_index] = objA glue_pos[g_index] = #() glue_set[g_index] = #() progressStart "Searching...." -- Look for a collision for i = 1 to a do ( pntA = arrA[1][i] prcnt = (i as float/a as float * 100.0) as integer progressUpdate prcnt loc = findItem arrB[1] pntA if loc == 0 then ( for j = 1 to arrB.count do ( pntB = arrB[1][j] diff = process_diff pntA pntB if diff < thres then ( loc = j exit ) ) -- loop ) -- if if diff < thres or loc != 0 then ( count = count + 1 append glue_set[g_index] loc append glue_pos[g_index] i append arry i ) ) -- loop i progressEnd() convertTomesh objB setvertselection objB glue_set[g_index] convertToMesh objA setvertselection objA arry gw = objA.name as string + "<-->" + objB.name as string if count > 1 then ( glue_names[g_index] = gw try ( glue_items.items = glue_names ) catch ( messagebox "Unknown Error" return 0 ) gw = "["+count as string+"]" p_count.text = gw ) count ) on store pressed do ( if classof objA == Editable_mesh and classof objB == Editable_mesh then copySel objA objB set_thres.value ) fn pasteSel = ( index = glue_items.selection count = 1 -- ReSelect the objects. Update the data. try ( select glue_objA[index] a = selection as array objA = a[1] ) catch ( messagebox "Please click COPY first to copy the vertex positions before performing a GLUE operation." title:"GLUE" return 0 ) try ( select glue_objB[index] a = selection as array objB = a[1] ) catch ( messagebox "Please click COPY first to copy the vertex positions before performing a GLUE operation." title:"GLUE" return 0 ) previous_b = #() try ( for i in glue_set[index] do ( append previous_b (getvert objB i) pnt = getvert objA glue_pos[index][count] setvert objB i pnt count = count + 1 ) update objB ) catch ( messagebox "You have made an invalid selection. This node has been deleted." title:"GLUE" ) ) on paste pressed do ( pasteSel() ) ) -- rolloutrollout surf_unwrap "Source Materials" ( local uvBMP, file, default_size = 256 fn createAttachFloater = ( try ( closeRolloutFloater attachFloater removeRollout attachFloater ) catch () attachFloater = newRolloutFloater "Mesh Attach" 175 300 270 150 addRollout attach_roll attachFloater ) fn createGlueFloater = ( try ( closeRolloutFloater glueFloater removeRollout glueFloater ) catch () glueFloater = newRolloutFloater "GLUE" 175 360 270 150 addRollout glue_roll glueFloater ) group "Parameters" ( PickButton pickUnwrap "<Surf>" width:100 PickButton pickY "=>" width:20 height:16 pos:[30, 60] editText grid_pos "Y Axis:" text:"0.0" fieldWidth:60 enabled:false pos:[54, 60] PickButton pickX "=>" width:20 height:16 pos:[30, 80] editText grid_Xpos "X Axis:" text:"0.0" fieldWidth:60 pos:[54, 80] enabled:false checkbox morph "Use Stack" checked:false pos:[30, 100] spinner set_width "Scale:" range:[0, 1, .1] scale:.1 type:#float width:60 align:#center ) group "Options" ( button unwrapSurf "Unwrap" width:50 across:2 tooltip:"Unwrap the mesh object." button restore "Restore" width:50 tooltip:"Restore the mesh object." button start_attach "Attach" width:50 across:2 tooltip:"Attachments Log" button start_glue "Glue" width:50 tooltip:"Glue Mesh Pieces together." ) button surf_help "Help" width:100 label disp_unwrap "" on start_attach pressed do createAttachFloater() on start_glue pressed do createGlueFloater() on surf_help pressed do ( messagebox "Use this utility to directlty unwrap a mesh or spline (Surface Tools) surface according to the postions of the X and Y planar grids. \n1. After non-circular, open sections of the mesh have been detached, draw the grids for the X and Y axis. \n2. Position the object whereas the X grid is in the middle and the Y grid is in front. \n3. Click the Unwrap button." ) on pickY picked nobj do ( grid_pos.text = nobj.pos.y as string select nobj ) on morph changed state do ( morph_state = state ) on pickX picked nobj do ( grid_Xpos.text = nobj.pos.x as string select nobj ) on set_width changed val do ( x_width = val ) on restore pressed do ( if object == true then restore_spline unwrap_obj old_spl disp_unwrap else restore_mesh unwrap_obj meshU_obj old_spl disp_unwrap ) on pickUnwrap picked nobj do ( local old_k, old_s -- Do not let the user panel reset itself again. rPressed = 1 unwrap_obj = nobj pickUnwrap.text =unwrap_obj.name try ( convertToSplineShape nobj unwrap_obj = nobj pnt = getKnotPoint unwrap_obj 1 1 object = true count = 1 old_s = numSplines unwrap_obj for i = 1 to old_s do ( old_k = numKnots unwrap_obj i for j = 1 to old_k do ( pnt = getKnotPoint unwrap_obj i j old_spl[count] = i as string + " " + j as string + " " + pnt.x as string + " " + pnt.y as string + " " + pnt.z as string count = count + 1 ) ) ) catch ( object = false pnt = [0, 0, 0] try ( -- Determine if it is a mesh object. old_k = getnumverts unwrap_obj for i = 1 to old_k do ( old_spl[i] = getvert unwrap_obj i ) ) catch ( -- This is not a mesh either. Try NURBS Point Surf... ) ) -- main catch if rPressed == 0 then ( grid_pos.text = unwrap_obj.pos.y as string grid_Xpos.text = pnt.x as string ) grid_pos.enabled = true grid_Xpos.enabled = true select unwrap_obj ) on unwrapSurf pressed do ( b_axis = grid_pos.text as float x_a = grid_Xpos.text as float thres = set_width.value if object == true then unwrap_spline unwrap_obj b_axis x_a thres disp_unwrap else ( meshU_obj = unwrap_mesh unwrap_obj b_axis x_a x_width morph.checked disp_unwrap ) ) ) -- rollout rollout map_roll "Mapping Types" ( group "Parameters" ( radiobuttons uvw_type "Mapping Type:" labels:#("Planar", "Cylindrical", "Spherical", "Shrink Wrap", "Box", "Face") default:1 radiobuttons align_type "Axis:" labels:#("X", "Y", "Z") default:1 ) on uvw_map changed val do ( map_type = val - 1 ) on align_type changed val do ( uvw_axis = val - 1 ) ) -- color_mat & global_color are global varibles used here. rollout edge_roll "Assign Edge Color" ( local l_obj, arry = #() fn assign_edges obj colA = ( l_obj = obj sel_index = sel_set.count + 1 if color_mat.count < 1 then ( f = getnumfaces obj * 3 for a = 1 to f do ( color_mat[a] = 0 ) ) -- Get EdgeSelection from Edit_Mesh or Editable_Mesh modifiers try ( arry = getEdgeSelection obj obj.modifiers[1] ) catch ( try ( arry = getEdgeSelection obj ) catch ( messagebox "Please add an Edit Mesh modifier to your mesh to select the edges!" return 0 ) ) if arry.count >= 1 then ( sel_set[sel_index] = arry sel_index = sel_index + 1 try ( for i in arry do ( color_mat[i] = colA ) ) catch ( messagebox "Please use an Edit Mesh modifier to select the edge." ) ) else messagebox "There was not an EdgeSelection Present." title:"Edge Colors" ) group "Source Data" ( colorpicker edge_color "Edge:" color:global_color offset:[0,0] pickbutton p_obj "SOURCE" width:80 height:25 button get_edges "-->Get<--" width:80 button unselect "Invisible Edges" width:80 tooltip:"Make unselected edges invisible." ) -- Code -1 means do not draw this edge. fn makeInvisible = ( try ( numE = getnumfaces l_obj ) catch ( messagebox "Please pick the SOURCE object first." return 0 ) numE = numE * 3 try ( for i = 1 to numE do ( if color_mat[i] == 0 then color_mat[i] = -1 ) ) catch ( messagebox "There is a problem with this node." ) ) on unselect pressed do ( makeInvisible() ) group "Color Assignments" ( label e_title "NumOfEdges:" across:2 label e_num "UNKNOWN" spinner c_set "Set:" type:#integer range:[1, 10000, sel_index] scale:1 width:80 enabled:false align:#center ) on c_set changed val do ( if val > sel_set.count then ( val = val - 1 c_set.value = val ) try ( convertToMesh l_obj setEdgeSelection l_obj sel_set[val] update l_obj select l_obj ) catch ( messagebox "Could not set the Edge Selection." title:"Assign Edge Colors" ) ) button reset "Reset" width:80 button e_help "Help!" width:80 on reset pressed do ( sel_set = #() sel_index = 0 c_set.value = 1 c_set.enabled = false color_mat =#() p_obj.text = "SOURCE" e_num.text = "UNKNOWN" global_color = white edge_color.color = white arry = #() ) fn setup nobj = ( if classof nobj == editable_mesh then ( assign_edges nobj edge_color.color gw = (getnumfaces nobj * 3) as string e_num.text = gw select nobj p_obj.text = nobj.name c_set.enabled = true ) else messagebox "Please use an EDIT MESH modifier to select an edge." title:"Edge Color Assignments" ) on p_obj picked nobj do ( setup nobj ) on get_edges pressed do ( a = selection as array setup a[1] ) on e_help pressed do ( messagebox "Use this utility to color the edges of a mesh so that a 'texture seam' does not appear." title:"Edge Color Assignments" ) ) --rollout -- this rollout is used to create a mesh based on the texture UVs... -- UV Mesher rollout uv_roll "Texture UVs->MESH" ( local obj, default_size = 256, obj_a, obj_b fn make_mesh iW iH iZ z_on face_sel = ( if classof obj != editable_mesh then ( return 0 ) numF = getnumfaces obj numV = getnumtverts obj tar = #() if z_on == false then iZ = 0.0 -- Get Face Selection if face_sel == true then ( try ( tar = getFaceSelection obj obj.modifiers[1] ) catch ( tar = setFaceArray tar numF messagebox "There was not a Face Selection present at the top of the modifier stack." title:"UV Mesher" ) ) else tar = setFaceArray tar numF Uobj = mesh numverts:numV numfaces:tar.count Uobj.pos = [0,0,0] for t in tar do ( -- get the faces try ( faceT = getTVFace obj t ) catch ( messagebox "This object does not have any UVW Mapping Applied." return 0 ) -- take the TVs out of the face index tva = gettvert obj faceT.x tvb = gettvert obj faceT.y tvc = gettvert obj faceT.z -- Translate the TVs to 3D Space positions. a = [(tva.x*iW),(tva.y*iH), (tva.z*iZ)] b = [(tvb.x*iW),(tvb.y*iH), (tvb.z*iZ)] c = [(tvc.x*iW),(tvc.y*iH), (tvc.z*iZ)] setvert Uobj faceT.x a setvert Uobj faceT.y b setvert Uobj faceT.z c setface Uobj t faceT.x faceT.y faceT.z e1 = getedgevis obj t 1 setedgevis Uobj t 1 e1 e2 = getedgevis obj t 2 setedgevis Uobj t 2 e2 e3 = getedgevis obj t 3 setedgevis Uobj t 3 e3 ) update Uobj Uobj.wirecolor = obj.wirecolor -- Delete Unnecessary verts from the node. zero_pnt = [0,0,0] as string count = 0 numV = getnumverts Uobj for y = 1 to numV do ( try ( pnt = getvert Uobj y if pnt as string == zero_pnt then ( deletevert Uobj y numV = numV - 1 count = count + 1 ) ) catch () ) gw = "There were "+count as string+" unnecessary vertices deleted from the node." messagebox gw title:"UV Mesher" --rotate Uobj 90 [1, 0, 0] ) fn deform_uvs objA objB iW iH iZ = ( local numT converttomesh objA converttomesh objB try ( getTVFace objA 1 ) catch ( messagebox "Please apply UVW Mapping to the SOURE object." title:"UV Mesher" return 0 ) numF = getnumfaces objA numF2 = getnumfaces objB if numF != numF2 then ( messagebox "These objects do not have an equal # of faces." title:"UV Mesher" return 0 ) converttomesh objA for i = 1 to numF do ( fa = getTVFace objA i fb = getFace objB i va1 = getvert objB fb.x va2 = getvert objB fb.y va3 = getvert objB fb.z if iZ != 0 then tz = va1.z/iZ else tz = 0 tpnt1 = [(va1.x/iW), (va1.y/iH), tz] if iZ != 0 then tz = va2.z/iZ else tz = 0 tpnt2 = [(va2.x/iW), (va2.y/iH), tz] if iZ != 0 then tz = va3.z/iZ else tz = 0 tpnt3 = [(va3.x/iW), (va3.y/iH), tz] settvert objA fa.x tpnt1 settvert objA fa.y tpnt2 settvert objA fa.z tpnt3 ) update objA ) fn morph_mesh objA objB = ( numF = getnumfaces objB numFa = getnumfaces objA if numFa < numF then ( messagebox "SOURCE Object does not contain enough faces to create the morph target." return 0 ) index_v = #() index_p = #() count = 1 progressStart "Analyzing...." for t = 1 to numF do ( calcy = (t as float / numF as float) * 100 calcy = calcy as integer progressUpdate calcy -- get the faces faceT = getface objB t -- take the Vertices out of the face index from TARGET tva = getvert objB faceT.x tvb = getvert objB faceT.y tvc = getvert objB faceT.z -- take Vertices out of the face index from SOURCE faceS = getface objA t setvert objA faceS.x tva setvert objA faceS.y tvb setvert objA faceS.z tvc -- Mark all the verts that are not moving. index_v[faceS.x] = 1 index_v[faceS.y] = 1 index_v[faceS.z] = 1 ) -- loop t progressEnd() update objA progressStart "Checking...." numV = getnumverts objA for i = 1 to numV do ( calcy = (i as float / numV as float) * 100 calcy = calcy as integer progressUpdate calcy if index_v[i] == undefined then ( index_p[count] = i count = count + 1 ) ) progressEnd() if index_p.count >= 1 then ( gw = index_p as string setvertselection objA index_p update objA gw = "I have found "+count as string+" incorrect vertices. Go to Editable Mesh->Sub-Object Vertex to delete the selection." messagebox gw title:"UV Mesher" ) select objA ) group "Source Objects" ( pickbutton p_obj "SOURCE" width:100 spinner mesh_sizeY "Length:" type:#float range:[1, 8000, default_size] scale:1 width:95 offset:[-2,0] enabled:register spinner mesh_sizeX " Width:" type:#float range:[1, 8000, default_size] scale:1 width:95 offset:[-5,0] enabled:register spinner mesh_sizeZ "Height:" type:#float range:[0, 8000, default_size] scale:1 width:95 offset:[-5,0] enabled:register checkbox z_on "Depth" checked:true align:#center button f_ap "APPLY" width:100 ) on z_on changed state do ( if state == false then ( mesh_sizeZ.enabled = false ) else mesh_sizeZ.enabled = true ) on p_obj picked nobj do ( type = classof nobj if classof nobj == Editable_mesh or type == sphere or type == cylinder then ( obj = nobj p_obj.text = obj.name select obj converttomesh obj -- Get Dimensions of the Diffuse Channel try ( map = obj.material.diffusemap file_sel = openBitmap map.filename mesh_sizeX.value = file_sel.width mesh_sizeY.value = file_sel.height ) catch () ) else ( messagebox "Please apply an Edit Mesh modifier to your mesh object." title:"UV Mesher" ) ) on f_ap pressed do ( if classof obj == editable_mesh then make_mesh mesh_sizeX.value mesh_sizeY.value mesh_sizeZ.value z_on.checked false else messagebox "Please select an Editable Mesh Object or Convert the Object to an Editable Mesh." ) group "Manipulate Surf" ( pickbutton obj1 "SOURCE" width:100 pickbutton obj2 "TARGET" width:100 button d_uv "WARP" width:45 height:25 across:2 align:#center tooltip:"Match SOURCE UV Coords to TARGET UV Mesh" button morph_uv "MORPH" width:45 height:25 tooltip:"Morph UV Mesh to SOURCE Object." ) on morph_uv pressed do ( if classof obj_a == editable_mesh and classof obj_b == editable_mesh then morph_mesh obj_a obj_b ) on obj1 picked nobj do ( type = classof nobj if classof nobj == editable_mesh then ( obj_a = nobj obj1.text = nobj.name select obj_a ) else ( messagebox "Please select an Editable Mesh Object or Convert the Object to an Editable Mesh." ) ) on obj2 picked nobj do ( type = classof nobj if classof nobj == editable_mesh or type == sphere or type == cylinder then ( obj_b = nobj obj2.text = nobj.name select obj_b converttomesh obj_b ) else messagebox "Please select an Editable Mesh Object or Convert the Object to an Editable Mesh." ) on d_uv pressed do ( if classof obj_a == editable_mesh and classof obj_b == editable_mesh then deform_uvs obj_a obj_b mesh_sizeX.value mesh_sizeY.value mesh_sizeZ.value else messagebox "Please select Editable Mesh Objects only." ) ) -- UV Renderer rollout rend_roll "UV Manager" ( local default_size = 256, file_sel fn createEdgeFloater = ( try ( closeRolloutFloater edgeFloater removeRollout edgeFloater ) catch () edgeFloater = newRolloutFloater "Assign Colors" 175 340 270 150 addRollout edge_roll edgeFloater ) group "Render Settings" ( colorpicker user_color "Wire:" color:white offset:[0,0] colorpicker mat_color "Mat:" color:black offset:[0,0] checkbox def_mat "Default Material" checked:false offset:[25,0] checkbutton use_stack "Use Stack" width:100 tooltip:"Use Face Selection from stack." --checkbutton light_stack "Highlight Faces" width:100 tooltip:"Highlight the Face Selection." spinner image_sizeY "Length:" type:#float range:[10, 8000, default_size] scale:1 width:95 offset:[-2,0] enabled:register spinner image_sizeX " Width:" type:#float range:[10, 8000, default_size] scale:1 width:95 offset:[-5,0] enabled:register button user_map "<NONE>" width:100 tooltip:"Choose the texture for the object." enabled:register button display_map "Display Image" width:100 tooltip:"Display Image." enabled:register button render_map "Render Map" width:100 tooltip:"Render the UV Map." button assign_map "Edge Colors" width:100 tooltip:"Assigns Colors to UV Edges." checkbox mapping "Include Bitmap" checked:false offset:[25,0] enabled:register ) on assign_map pressed do ( -- move user_color to the global color global_color = user_color.color createEdgeFloater() ) on display_map pressed do ( try ( display file_sel ) catch ( messagebox "There is no image stored in the buffer." ) ) on user_map pressed do ( filename = selectBitMap caption:"Select Bitmap Texture" file_sel = filename --image_sizeX.value = file_sel.width --image_sizeY.value = file_sel.height --try ( if file_sel != undefined then ( arry = get_all_tokens file_sel.filename " " user_map.text = arry[arry.count] ) else file_sel = "" ) on cancel_op pressed do ( shutDown=true progressEnd() ) on def_mat changed val do ( try ( obj_array = selection as array unwrap_obj = obj_array[1] classify = classof unwrap_obj.material if val == true then mat_color.enabled = false else mat_color.enabled = true if val == true and classify == Standardmaterial then ( try ( mat_color.color = unwrap_obj.material.diffuse map = unwrap_obj.material.diffusemap file_sel = openBitmap map.filename image_sizeX.value = file_sel.width image_sizeY.value = file_sel.height arry = get_all_tokens file_sel.filename " " user_map.text = arry[arry.count] ) catch () ) else ( messagebox "This feature presently only works with Standard Materials." mat_color.enabled = true def_mat.checked = false ) ) catch ( messagebox "You have selected an invalid object." ) ) -- group fn uv_rend = ( err = 0 obj_array = grab_sel() unwrap_obj = obj_array[1] tv_count = 0 -- # of tverts. if unwrap_obj == undefined then ( return 0 ) try ( g = getnumverts unwrap_obj ) catch ( convertToMesh unwrap_obj ) -- initialize the edge color array..... progressStart "Analyzing........" f = getnumfaces unwrap_obj f = f * 3 try ( if color_mat.count != f then ( color_mat = #() for i = 1 to f do ( calcy = (i as float / f as float) * 100 calcy = calcy as integer if calcy < 1 then calcy = 1 progressUpdate calcy color_mat[i] = 0 ) ) ) catch () progressEnd() try ( tv_count = getnumtverts unwrap_obj ) catch ( ) mod_n = check_stack uvwmap unwrap_obj gw = classof mod_n -- if uvwmap is not found, then add it w/planar projection. if gw != uvwmap then ( if tv_count <= 0 then ( addModifier unwrap_obj (uvwmap maptype:map_type) mod_n = unwrap_obj.modifiers[1] ) ) -- if --try --( err = 1 tv_count = getnumtverts unwrap_obj if mapping.checked then uvBMP = file_sel else ( if user_map.text != "<NONE>" then ( uvBMP = bitmap file_sel.width file_sel.height ) else uvBMP = bitmap image_sizeX.value image_sizeY.value setMaterial uvBMP mat_color.color ) -- else arr = user_color.color arr.alpha = 255.0 u_color = #(arr) err = 2 if tv_count > 0 then render_uvw unwrap_obj uvBMP (u_color)(use_stack.checked) else messagebox "This object must be mapped." --) --catch ( -- messagebox "Cannot Render." title:"UV Manager" --) ) -- function on render_map pressed do ( uv_rend() ) -- func ) -- rollout rollout match_roll "Source Materials" ( local target, source group "" ( pickbutton pick_tar "<TARGET>" width:100 pickbutton pick_source "<SOURCE>" width:100 checkbutton deform_tverts "Match Tverts" width:100 checked:false tooltip:"Match Texture Vertices Also." button help "Help" width:100 button start_util "Apply" ) on help pressed do ( messagebox "Use this routine to copy material and UVW coordinates (if Match Tverts is checked) from one object to another." ) on pick_tar picked nobj do ( target = nobj pick_tar.text = nobj.name ) on pick_source picked nobj do ( source = nobj pick_source.text = nobj.name ) on start_util pressed do ( match_uvw target source deform_tverts.checked ) ) -- rollout rollout cam_roll "Projection Render" ( local obj, mod, c_width = 85 local my_cam, my_texture, cam_set = #() local offset_w = 10.0, offset_h = 10.0 -- This func automatically adjusts the camera lens to "fit" the mesh. The Y and Z -- fields are considered. fn adjust_lens = ( ) group "Reference Information" ( pickbutton pick_obj "<SOURCE>" width:c_width enabled:register spinner o_w "Width:" type:#float range:[0, 16000, 0] scale:.1 width:95 offset:[-5,0] enabled:false spinner o_l "Length:" type:#float range:[0, 16000, 0] scale:.1 width:95 offset:[-5,0] enabled:false spinner o_h "Height:" type:#float range:[0, 16000, 0] scale:.1 width:95 offset:[-5,0] enabled:false ) on pick_obj picked nobj do ( obj = nobj pick_obj.text = obj.name mod = check_stack uvwmap obj gw = classof mod -- if uvwmap is not found, then add it w/planar projection. if gw != uvwmap then ( addModifier obj (uvwmap maptype:0) mod = obj.modifiers[1] ) o_w.value = mod.width o_h.value = mod.height o_l.value = mod.length ) group "Camera (Manual Focus)" ( button sel_cam "Select Camera" width:c_width tooltip:"Select the user's camera." button rend_cam "Render Cam" width:c_width tooltip:"Render the Camera" --spinner x_cam "X:" type:#float range:[0, 16000, 0] scale:.1 width:85 offset:[-20,0] enabled:false --spinner z_cam "Z:" type:#float range:[0, 16000, 0] scale:.1 width:85 offset:[-20,0] enabled:false checkbox rend_view "Render" checked:true offset:[33, 0] ) on rend_cam pressed do ( t_l = o_w.value + offset_w t_h = o_l.value + offset_h if cam_set != undefined then ( try ( my_texture = render camera:cam_set[1] \ frame:currenttime \ outputwidth:t_l \ outputHeight:t_h \ showcone:true \ vfb:rend_view.checked ) catch ( return 0 ) ) ) fn assign_cam = ( local name_cam a = grab_sel() try ( name_cam = classof a[1] ) catch ( messagebox "Please reselect the camera set." return false ) if a.count < 2 then ( messagebox "Please select both the camera and the camera.target..." return false ) if name_cam == targetcamera then ( cam_set = a select cam_set -- Adjust the camera position (adjust both camera objects). cam_dist = (o_l.value / 4) + o_w.value if obj == undefined then ( messagebox "Please select the object." return false ) cam_set[1].pos = [obj.center.x, (0 - cam_dist), obj.center.z] cam_set[2].pos = [obj.center.x, obj.center.y, obj.center.z] -- This rotation value was taken from the MAXscripter Selection Array. rotate cam_set[1] (quat 1 0 0 1) rotate cam_set[2] (quat 1 0 0 1) ) return true ) -- Adjust the camera lens to focus on the target. fn focus_lens = ( thres = 0.1 scale_value = .01 -- Scale the object until it is within bounds. for i = 1 to 300 do ( ns = cam_set[1].scale if cam_set[1].max.z < (obj.max.z + thres) and cam_set[1].max.z > (obj.max.z - thres) then ( --print "EXIT Z" --if cam_set[1].max.z < obj.max.z then cam_set[1].scale = [ns.x, ns.y + .01, nz] exit ) --gw = "NS:"+ns as string+" obj_max:"+obj.max as string+" cam:"+cam_set[1].max as string --print gw if obj.max.z > cam_set[1].max.z then cam_set.scale = [ns.x, ns.y + scale_value, ns.z] else cam_set.scale = [ns.x, ns.y - scale_value, ns.z] ) for i = 1 to 300 do ( ns = cam_set[1].scale if cam_set[1].max.x < (obj.max.x + thres) and cam_set[1].max.x > (obj.max.x - thres) then ( --print "EXIT X" --if cam_set[1].max.x < obj.max.x then cam_set[1].scale = [ns.x, ns.y + .5, nz] exit ) ns = cam_set[1].scale --gw = "NS:"+ns as string+" obj_max:"+obj.max as string+" cam:"+cam_set[1].max as string --print gw if obj.max.x > cam_set[1].max.x then cam_set.scale = [ns.x + scale_value, ns.y, ns.z] else cam_set.scale = [ns.x - scale_value, ns.y, ns.z] ) ) on sel_cam pressed do ( status = assign_cam() if status == true then focus_lens() ) ) -- rollout fn createSurfFloater = ( try ( closeRolloutFloater surfFloater removeRollout surfFloater ) catch () surfFloater = newRolloutFloater "Flatten Surface" s_width s_height 250 150 addRollout surf_unwrap surfFloater ) fn createMatFloater = ( try ( closeRolloutFloater matFloater removeRollout matFloater ) catch () matFloater = newRolloutFloater "UVW Match" 155 225 250 150 addRollout match_roll matFloater ) fn createCamFloater = ( try ( closeRolloutFloater camFloater removeRollout camFloater ) catch () camFloater = newRolloutFloater "Planar Projection" 175 280 250 150 addRollout cam_roll camFloater ) fn createRendFloater = ( try ( closeRolloutFloater rendFloater removeRollout rendFloater ) catch () rendFloater = newRolloutFloater "UV Manager" 175 375 250 150 addRollout rend_roll rendFloater ) fn createUVMesh = ( try ( closeRolloutFloater uvFloater removeRollout uvFloater ) catch () uvFloater = newRolloutFloater "UV Mesher" 175 350 270 150 addRollout uv_roll uvFloater ) group "" ( button surf_start "Flatten Surf" width:b_width height:20 tooltip:"Surface Flatten Utility" button rend_start "UV Manager" width:b_width height:20 tooltip:"Render UVW Coordinates to a Bitmap." button mat_start "Project UVW" width:b_width height:20 tooltip:"Copy material/UVW from one object to another." button build_mesh "UV Mesher" width:b_width tooltip:"Create a Mesh based on UV Coordinates." button cam_start "Camera Projection" width:b_width height:20 tooltip:"Render Surface to Camera." button update_map "Update Map" width:b_width height:20 tooltip:"Update the Texure." enabled:register ) group "About" ( label t1 " UV Layout for MAX" align:#left label t3 " Release 1.5" align:#left label t4 " \xa9 2001 Richard Osborne" align:#left label t5 " Kreation's Edge" align:#left button helpButton "Online Help (.chm)" width:100 height:25 ) on update_map pressed do ( mode = test_script() update_texture unwrap_obj junk_bitmap ) on build_mesh pressed do ( mode = test_script() createUVMesh() ) on skin_start pressed do ( mode = test_script() createSkinFloater() ) on surf_start pressed do ( mode = test_script() createSurfFloater() ) on mat_start pressed do ( mode = test_script() createMatFloater() ) on rend_start pressed do ( mode = test_script() createRendFloater() ) on cam_start pressed do ( mode = test_script() createCamFloater() ) on helpButton pressed do ( local leng = 0, file_name -- store the path_Name length. local status, f_path = "", root_path = "" f = read_data "uv_max/layout.ini" 2 try ( leng = f[1].count file_name = f[1] if file_name=="undefined" or file_name.count < 1 then leng = 0 else f_path = getFilenamePath file_name ) catch ( leng = 0 ) if leng < 1 then ( file_name = getOpenFileName caption:"Find the UV Layout Online Help (.chm) File..." if file_name != undefined then save_data "layout.ini" file_name 2 ) if file_name != undefined then ( try ( com = "hh "+file_name status = DOScommand com ) catch () ) ) )stitch them together.. only 40000 character per post..
I second this. My roadkill exe isn't named "roadkill.exe" so maybe that's the problem, I didn't rename it, and I wouldn't figure that name would be hardcoded like that, I can't get it to work either though. Still been just exporting and reimporting. Which is messy of course.
Can i fix it? If it is, where i can see the pack function?
Is there some specific way I need to use this?
The UV-Island->Smoothing Group is in the little 'Tools' rollout above the Edit UV button. Are you using the "Smoothing groups from UV Shells" under this toolset?
Is there something I might be missing? Thanks.
I guess my problem now is that I've already rigged my model. If I add the Editable Poly modifier wont that screw up my weights and stuff?
use skin tools in utilities to extract skin weights, then you can change the mesh to edit poly, re-apply skin, import the weights back from the skindata and you should be sorted. Good luck!
it should be fine. converting from a mesh to editable poly doesnt change vertex indexes. which is what skin works with.
Some functions (texel ratio related) do expect however the base object to be a editable poly with the same tris and face count as the latest state of the modifier stack- its things like these that make it easier to make scripts for apps such as modo.
and just to share a bit of my findings using non-programmer skills... i wanted to keep polygons or quads on the mesh after its been unwrapped with roadkill ( thus exported to obj and imported back ), rather than have the messy triangles (under max 2009)
this script "fn_40__unwrap_with_roadkill.ms" changes the OBJ export settings to use triangles.
so to use quads you need to change the 13th value in this line from 0 to 1
to use polygons you need to change the 13th value in this line to 0 to 2
fnWriteBinary outputCfgPath #(16, 0, 0, 0, 86, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -128, 63, 0, 0, 1, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 46, 47, 109, 97, 112, 115, 47, 0, 1, 0, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 3, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 60, 78, 79, 78, 69, 62)
it took me 2hrs to figure it out so it might save some other artist some time
im sure a programmer would do this in a few seconds
thanks again for the scripts.
roadkill gives very different results for some objects with often unusable results
so triangles it is.
I keep getting this error when I use the create block out map.
"--No ""display"" function for undefined"
Doesn't matter what option I select (clipboard, display, save as). All the other texture maps work just fine.
Using Max 2012.
Thanks.
a very little thing , the get/set texel density works only for UV channel 1,regardless of what UV channel I set to be edited
It's a bit annoying as I would particularly like this option to normalize my uv's for a lightmap packing. For now i have to switch my channels which is lil tedious.
Thanks for helping.
Regardless, great toolset and I've been using it since it's initial release. It's most welcomed
- aligning shells to each other
- aligning a single shell to the edge you've selected (rotates the shell so that edge is straight). very cool.
- it's one-click relax button always gives me cooler results
- uv shell to smoothing group
- and yeah, various masks
Yeaaaaaaahhh... true dat ;p For example normalizing uv shells in 3ds max pretty much... doesn't work. Yeah it kinda works but TexTools uses math to do this... 3ds max guesses...at least thats what the result looks like.
Any suggestion?
I'm wondering if there are any plans to let the Texel Density be measured in Pixels/WorldUnit instead of Pixels/MaxUnit - I measure things in meters and it is a hassle to constantly do the math to keep the proper proportions of things.
But I also stated hopefully clear enough in the past that the MZP textools file is just a ZIP so you can open it and explore the scripts, use them for other scripts and or improve them with the new 3dsMax 2012 or greater versions.
I got an email back in 2011 I think from Autodesk that they would like to take some ideas and tools from TexTools into 3dsMax and I believe my credits should be within 3dsMax 2012 because of the UV tools. For me though that was a sign that they finally addressed the UV tools and there is not much need for me trying to fix max with TexTools.
Shouldn't be to hard to open the script and look into it yourself. I know that maxscript has some convertion tools for the units. Personally I work closer with Game Engines that I write or modify myself, which is why real units (internal units) matter as the real deal. But for some people that use UDK, Unity etc. and that prefer unit conversions (unity scales e.g by default units to 0.01).
If you are not that great or skilled in Maxscript, try to find at least the script within the TexTools.mzp (zip file), make a thread here in Tech talk on polycount and ask people to help you with it so works with set units in Max.