Wire Chaos
A 3dsMax-Tool written in MaxScript
April 24, 2017 - Tommy Dräger
Wire Chaos is a script that lets you distribute a point cloud along the surface of any given mesh. Afterwards the points gets randomly connect to bezier-splines. Those splines are manipulated by the gravity parameter (also compatible for inverse gravity). Have fun with the script.
Download:
simply extract it inside the scripts folder of 3dsMax
increase & decrease gravity
increase & decrease count of wires
distribute the points on the surface of any object
WireChaos GUI in 3dsMax
WireChaosGUI.ms
rollout WireChaos "WireChaos" width:184 height:248
(
groupBox SimulationGroup "Simulation" pos:[8,152] width:168 height:64
button solve "Solve" pos:[16,168] width:152 height:40
spinner gravity_spinner "" pos:[88,120] width:75 height:16 type:#integer range: [-1000,1000000,10]
spinner count_spinner "" pos:[88,96] width:75 height:16 type:#integer range: [0,100000,100]
groupBox ParameterGroup "Parameter" pos:[8,80] width:168 height:64
label lbl1 "Count" pos:[24,96] width:56 height:16
label lbl2 "Gravity" pos:[24,120] width:56 height:16
pickButton ctm_btn "Pick Object" pos:[16,24] width:152 height:40 enabled:true
groupBox customMeshGroup "Custom Mesh" pos:[8,8] width:168 height:64
label footer "(c) FenixFox®Studios 2017" pos:[8,224] width:168 height:24
fn GenerateRandomPointsOnSurface obj point_count =
(
local custom_mesh = snapshotasMesh obj
local mesh_face_count = custom_mesh.numfaces
local point_array = #()
local area_size = 0
-- calculate the size from the area of faces
for i = 1 to mesh_face_count do
(
-- getFaceArea <Mesh mesh> <facelist>
area_size += meshop.getFaceArea custom_mesh i
)
-- for an even distibution of the points on the surface
local point_density = point_count / area_size
-- prepare an array for a random face selection
-- simple array shuffle routine here
local face_indicies = for i = 1 to mesh_face_count collect i
local random_indicies = #()
-- randomly transfer the elements from array A to B
while face_indicies.count > 0 do
(
random_element = random 1 face_indicies.count
-- now transfer by cutting from A to B
append random_indicies face_indicies[random_element]
deleteItem face_indicies random_element
)
-- initialize a counter for the created points
local current_point = 0
-- itterate trough the random faces -->(r_face) and create all points
for r_face in random_indicies while current_point < point_count do
(
-- calculate a limit of points for each face
local max_dense = ceil((meshop.getFaceArea custom_mesh r_face) * point_density)
-- getFace <mesh> <face_index_integer>
-- returns vert_index as integer[3]
local face = getFace custom_mesh r_face
-- getVert <mesh> <vert_index_integer>
-- returns vertex as pos3
-- those verticies are going to use later as "barycentric coords" [use wiki]
local vert1 = (getVert custom_mesh face.x)
local vert2 = (getVert custom_mesh face.y)
local vert3 = (getVert custom_mesh face.z)
--loop from 1 to the number of max allowed points on the face
for i = 1 to max_dense while current_point < point_count do
(
-- save it later to an array
local n_obj = point wirecolor:green
-- the normalvector information stored in his matrix are assign to the TM
n_obj.transform = matrixFromNormal (getFaceNormal custom_mesh r_face)
-- craete a randomized X and Y
local x = random 0.0 1.0
local y = random 0.0 1.0
--if the sum is greater than 1, subtract them from 1.0
if x + y > 1.0 do
(
x = 1.0 - x
y = 1.0 - y
)
--the third bary coord is 1.0 minus the other two
local z = 1.0 - x - y
--set position using barycentric coords.
-- add all 3 stretched/squeezed verts together
n_obj.pos = (vert1*x + vert2*y + vert3*z)
current_point += 1
append point_array n_obj.pos
delete n_obj
) -- for i loop end
) -- for r_face loop end
return point_array
) -- function end
-- shuffle array function
fn shuffle arr =
(
for counter = arr.count to 1 by -1 collect (swap arr[random 1 counter] arr[counter])
)
fn GenerateSplines p_array =
(
local points = shuffle p_array
local grav = gravity_spinner.value
local limit = points.count - (mod points.count 2)
local chroma = #( [0,104,55],
[48,163,85],
[120,198,122],
[193,230,153],
[255,253,206])
for i = 1 to limit by 2 do
(
local p1 = points[i]
local p2 = points[i+1]
s = splineShape()
addnewspline s
addKnot s 1 #corner #curve p1
addKnot s 1 #corner #curve p2
updateShape s
setKnotType s 1 1 #bezier
setKnotType s 1 2 #bezier
in_vec = p1 - [0,0,grav]
out_vec = p2 - [0,0,grav]
setOutVec s 1 1 in_vec
setInVec s 1 2 out_vec
s.adaptive = true
s.wirecolor = chroma[random 1 5]
--s.wirecolor = color 255 240 0
s.pivot = s.center
updateShape s
)-- for i end
)-- function end
on solve pressed do
(
if getnodebyname(ctm_btn.text) != undefined do
(
obj = getnodebyname(ctm_btn.text)
p = GenerateRandomPointsOnSurface obj count_spinner.value
GenerateSplines p
)
)
on ctm_btn picked obj do
(
fn shape_filt obj = isKindOf obj Geometry
if obj != undefined do
(
ctm_btn.text = obj.name
)
)
-- dotnet hack to change the icon of the rollout
on WireChaos open do
(
d = (windows.getChildHWND 0 WireChaos.title)[1]
WM_SETICON = 0x0080
ICON_SMALL = 0
icon = dotnetobject "System.Drawing.Icon" (getdir #scripts +
"\WireChaos\icon\favicon.ico")
windows.SendMessage d WM_SETICON ICON_SMALL icon.handle
)
)
createdialog WireChaos 184 248