[Closed] Speeding up a script.
Fine be that way, atleast it gives a little more info on what you need to supply.
-Eric
How about this:
Instead of going with the edit_poly modifier, creating a snapshot and converting it to editable poly object, this way you don’t have to deal with selecting a few faces, assigning the material ids to them, selecting other faces etc.
Try this code:
(
fn getElements obj faces:#All =
(
local objClass = classOf obj
local func
case objClass of (
Editable_Poly: fn func obj face = polyOp.getElementsUsingFace obj face
PolyMeshObject: fn func obj face = polyOp.getElementsUsingFace obj face
Editable_Mesh: fn func obj face = meshOp.getElementsUsingFace obj face
Edit_Poly: fn func obj face = (local elem = #{}; obj.getElementsUsingFace elem #{face} #{}; elem)
default: undefined
)
local elements = #()
local usedFaces = #{}
local numFaces = case objClass of (
Editable_Poly: obj.numFaces
Editable_Mesh: obj.numFaces
PolyMeshObject: obj.numFaces
Edit_Poly: obj.getNumFaces()
default: 0
)
if faces == #All then
faces = #{1..numFaces}
for f in faces where not usedFaces[f] do (
local elem = func obj f
append elements elem
usedFaces += elem
)
elements
)
fn assignRandomMatID obj =
(
with redraw off (
local newMesh = snapshot obj
convertToPoly newMesh
local roofIDRange = [81,99]
local buildingIDRange = [20,80]
local sel = polyOp.getFaceSelection newMesh
local elements = getElements newMesh faces:sel
for elem in elements do (
polyOp.setFaceMatID newMesh elem (random buildingIDRange[1] buildingIDRange[2])
)
for f in sel do (
polyOp.setFaceMatID newMesh f (random roofIDRange[1] roofIDRange[2])
)
)
newMesh
)
-- gc()
t = timeStamp()
assignRandomMatID $
format "took: % ms.
" (timeStamp() - t)
)
I didn’t test you code, but from my own tests it was way faster than tests I did with edit poly modifier. On my machine (not a very strong one) it took 22 secs. to assign random material ids to a plane with 20×20 segments, which translates into 49,430 polys after the greeble and edit poly modifier.
To run my script, create a plane, add greeble and check “select tops” and run the script. No need to add an edit poly modifier.
cheers,
o
Thanks Ofer_z! That works well, very fast, took 1000000ms on my machine for a 300,000 poly object!
Unfortunetly I’ve got to slow down the script as need to add one bit…
If the Id of the top face is 2 then the element needs to be given a different range of ID as I want the top bits of greeble to like aircon/roof objects, so can’t have them having the same ID as the main buildings.
Going to attempt to code this in myself now, but i’m just trying to disect your code! Looks very efficient and a bit over my head at the moment!
Script leaks memory like a siv though! Used 14.5gb Page file usage and the gc() didn’t fix this at the end of the script, had to restart max.
Rorschach is right, the script is not exactly leaking memory, it’s max’s undo buffer that gets filled. Turning off the undo buffer solves this and increases the speed.
(
fn getElements obj faces:#All =
(
local objClass = classOf obj
local func
case objClass of (
Editable_Poly: fn func obj face = polyOp.getElementsUsingFace obj face
PolyMeshObject: fn func obj face = polyOp.getElementsUsingFace obj face
Editable_Mesh: fn func obj face = meshOp.getElementsUsingFace obj face
Edit_Poly: fn func obj face = (local elem = #{}; obj.getElementsUsingFace elem #{face} #{}; elem)
default: undefined
)
local elements = #()
local usedFaces = #{}
local numFaces = case objClass of (
Editable_Poly: obj.numFaces
Editable_Mesh: obj.numFaces
PolyMeshObject: obj.numFaces
Edit_Poly: obj.getNumFaces()
default: 0
)
if faces == #All then
faces = #{1..numFaces}
for f in faces where not usedFaces[f] do (
local elem = func obj f
append elements elem
usedFaces += elem
)
elements
)
fn assignRandomMatID obj =
(
with undo off with redraw off (
local newMesh = snapshot obj
convertToPoly newMesh
local roofIDRange = [81,99]
local buildingIDRange = [20,80]
local sel = polyOp.getFaceSelection newMesh
local elements = getElements newMesh faces:sel
for elem in elements do (
polyOp.setFaceMatID newMesh elem (random buildingIDRange[1] buildingIDRange[2])
)
for f in sel do (
polyOp.setFaceMatID newMesh f (random roofIDRange[1] roofIDRange[2])
)
)
newMesh
)
-- gc()
t = timeStamp()
assignRandomMatID $
format "took: % ms.
" (timeStamp() - t)
)
I’ll try to upload a version with some comments later when I have some time, so it’ll be easier to understand what I did there.
Cheers,
o
I’ve added some comments, hopefully it will make it a bit more readable.
(
-- This function returns and array of bitArrays, each holding the
-- face IDs of the element.
-- If faces is supplied with a bitArray, only the face IDs in that
-- bitArray are checked for their elements.
fn getElements obj faces:#All =
(
-- Check the obj class
local objClass = classOf obj
-- Create a temp function (func) that given the object and a face ID returns
-- the element face IDs.
-- Depending on the class of obj, use the right method to get the element faces.
local func
case objClass of (
Editable_Poly: fn func obj face = polyOp.getElementsUsingFace obj face
PolyMeshObject: fn func obj face = polyOp.getElementsUsingFace obj face
Editable_Mesh: fn func obj face = meshOp.getElementsUsingFace obj face
Edit_Poly: fn func obj face = (local elem = #{}; obj.getElementsUsingFace elem #{face} #{}; elem)
default: undefined
)
-- Initialize some variables
local elements = #() -- The array of the elements
local usedFaces = #{} -- A bitArray to hold all the face IDs that were either checked or were part of elements of other checked faces.
-- Get the number of faces in obj (use the right method based on the class of obj).
local numFaces = case objClass of (
Editable_Poly: obj.numFaces
Editable_Mesh: obj.numFaces
PolyMeshObject: obj.numFaces
Edit_Poly: obj.getNumFaces()
default: 0
)
-- If faces is not a bitArray, make it into a bitArray with all the faces in obj.
if faces == #All then
faces = #{1..numFaces}
-- Loop through all the faces in the faces variable.
-- If the current face ID is already in usedFaces, skip it.
for f in faces where not usedFaces[f] do (
-- Using the temp function func, get the face IDs of the
-- element the current face is in.
local elem = func obj f
-- Append the element's face IDs to the elements array.
append elements elem
-- Add the face IDs in the element to the usedFaces bitArray.
usedFaces += elem
)
-- Return the elements array.
elements
)
-- This function creates a snapshot of the given object (the greeble object),
-- it then assigns random material IDs to the different elements, and different
-- material IDs to the roofs (the object's selected faces).
-- Finally, it returns the newly created object (the snapshot).
fn assignRandomMatID obj =
(
-- Turn off the undo buffer and scene redraw to save memory and increase speed.
with undo off with redraw off (
-- Create a snapshot of the original object.
local newMesh = snapshot obj
-- Convert the new snapshot into an editable_poly (editable_poly is much faster than
-- editable_mesh when it comes to a lot of geometry related scripted operations)
convertToPoly newMesh
-- Set local varialbes to define the ID ranges for the roofs and buildings.
local roofIDRange = [81,99]
local buildingIDRange = [20,80]
-- Get the current face selection from the new snapshot (this will be the same as
-- the current face selection on the original object.)
local sel = polyOp.getFaceSelection newMesh
-- Get newMesh's elements (the buildings). Supply the faces parameter with
-- the current selection to speed up finding elements.
local elements = getElements newMesh faces:sel
-- Loop through the elements, and assign a random material ID
-- to all the faces in that element.
for elem in elements do (
polyOp.setFaceMatID newMesh elem (random buildingIDRange[1] buildingIDRange[2])
)
-- Loop through the selected faces, and assign a random material ID
-- to it.
for f in sel do (
polyOp.setFaceMatID newMesh f (random roofIDRange[1] roofIDRange[2])
)
)
-- Return the snapshot mesh.
newMesh
)
-- gc()
t = timeStamp()
assignRandomMatID $
format "took: % ms.
" (timeStamp() - t)
)
Cheers,
o