Wall Maker v2.0 update
- anderson Yang
- Apr 4, 2023
- 7 min read
2023 March Update:
1. Material added, users can replace the path of HDRI map for the skydome light.
2. Created button for automatically assigning a default materials to the bricks, which includes OSL Shader based noise pattern , displacement, and aiStandardSurface.
3. New GUI control.
4. Python in Class hierarchy
A Python script for create brick wall. This script incluides two options to choose, one is cross arrangement, the other one is without arrangement. Users can also adjust how many bricks of row and column, the minimum/maximun is 1 and 9999. There are 3 different bricks that users can choose, solid bricks, Two-holes Hollow bricks, and Three-holes Hollow bricks. And the wall is RBD ready.

Python Module
"""Script for Maya
Version 02
Author: Anderson Yang
Date: 03/17/23
Contacts:
-Instagram: yang_vfx
-Mail: besthans0127@gmail.com
-LinkedIn: https://www.linkedin.com/in/hanyang0127/
Description: This code will generate bricks and wall, and if selecting the arrangement option, it can also generate cross structure wall.Also, Assigning material and environment light with Arnold renderer.
Tested on Maya 2022.3
"""
import maya.cmds as mc
import random
class OptionsWindow(object):
"""OptionWindow() base class definition is creating a window with
three buttons on the bottom. Users should be subclassing OptionsWindow()
and implementing callback functions, implementing displayOptions() method
to display custom GUI controls"""
def __init__(self):
self.window = "Wall Maker"
self.title = "Wall Maker"
self.size = (300,500)
self.actionName = "Apply and Close"
self.applyName = "Apply"
def create(self):
if mc.window(self.window,exists=True):
mc.deleteUI(self.window,window=True)
self.window = mc.window(self.window, title=self.title,widthHeight=self.size,menuBar=True)
self.mainForm = mc.formLayout(nd=100)
self.commandMenu()
self.commonButtons()
self.optionsForm = mc.formLayout(nd=100)
mc.formLayout(self.mainForm,e=True,
attachForm=([self.optionsForm,"top",0],
[self.optionsForm,"left",2],
[self.optionsForm,"right",2]),
attachControl=([self.optionsForm,"bottom",5,self.applyBtn]))
self.displayOptions()
mc.showWindow()
def commandMenu(self):
"""
commandMenu(self) method is adding pull down menu to the main window menu bar
"""
self.editMenu = mc.menu(label = "Edit")
self.editMenuTri = mc.menuItem(label = "Triangulate", command = self.editMenuTriCmd)
self.editMenuQuad = mc.menuItem(label = "Quadrangulate", command = self.editMenuQuadCmd)
self.helpMenu = mc.menu(label = "Help")
self.helpMenuItem = mc.menuItem(label = "Bugs Report on %s" % (self.title), command = self.helpMenuBugsCmd)
self.helpMenuAbout = mc.menuItem(label = "About", command = self.helpMenuAboutCmd)
def helpMenuBugsCmd(self,*args):
mc.launch(web = "https://forms.gle/xLMFcqLyyB1rmEaK6")
def editMenuSaveCmd(self,*args):pass
def editMenuResetCmd(self,*args):pass
def helpMenuAboutCmd(self, *args):
aboutInformation = mc.window(title = "Wall Maker for Maya by Anderson Yang", wh = (512, 200))
mc.rowColumnLayout(adjustableColumn = True, backgroundColor = (0.15, 0.15, 0.15))
mc.text(label = "Wall Maker for Maya 1.0 © 2023 Anderson Yang 02-13-2023")
mc.separator(height = 50, style = "out")
mc.text(label = "This is the first release of the Wall Maker for Maya.\n Please report any bugs or requests, and stay tuned for the full release.")
mc.separator(height = 50, style = "out")
mc.text(label = "Feel free to contact me directly about any issue at besthans0127@gmail.com \n ")
mc.showWindow(aboutInformation)
def actionCmd(self,*args):
"""Call back function that should be implemented in a subclass"""
# print( "ACTION" )
self.createWall()
mc.deleteUI(self.window,window=True)
def applyBtnCmd(self,*args):
"""Call back function that should be implemented in a subclass"""
#print( "APPLY" )
self.createWall()
def closeBtnCmd(self,*args):
mc.deleteUI(self.window,window=True)
def editMenuTriCmd(self, *args):
sel = mc.ls(selection = True)
for i in sel:
mc.polyTriangulate(i)
def editMenuQuadCmd(self, *args):
sel = mc.ls(selection = True)
for i in sel:
mc.polyQuad(i)
def assignMatCmd(self, *arg):
self.assigntMaterial()
mc.select("*Brick*")
mc.hyperShade(assign = brickSurface)
def assignHdriCmd(self, *arg):
self.assignHdri()
def commonButtons(self):
"""
commonButtons(self) method will edit layout self.mainForm and add three buttons on the bottom: action, apply, close
"""
self.commonBtnSize=(self.size[0]-18/3,26)
self.acctionBtn=mc.button(label=self.actionName,height=self.commonBtnSize[1], command = self.actionCmd)
self.applyBtn=mc.button(label=self.applyName,height=self.commonBtnSize[1],command=self.applyBtnCmd)
self.closeBtn = mc.button(label="Close",height=self.commonBtnSize[1],command=self.closeBtnCmd)
mc.formLayout(self.mainForm, e=True,
attachForm=([self.acctionBtn,"left",5],[self.acctionBtn,"bottom",5],[self.applyBtn,"bottom",5],[self.closeBtn,"bottom",5],[self.closeBtn,"right",5]),
attachPosition=([self.acctionBtn,"right",1,33],[self.closeBtn,"left",0,67]),
attachControl=([self.applyBtn,"left",4,self.acctionBtn],[self.applyBtn,"right",4,self.closeBtn]),
attachNone=([self.acctionBtn,"top"],[self.applyBtn,"top"],[self.closeBtn,"top"]))
def displayOptions(self):
""" displayOptions() All custom UI controlls should be added to this method
These controlls will be childred of the formLayout whose name is self.optionsForm """
# Frame layout
mc.setParent("..")
self.layout1 = mc.columnLayout(adj = True)
mc.formLayout(self.mainForm, e= True, attachForm=([self.layout1, "left", 0],[self.layout1, "right", 0]))
# Brick framlayout
self.brickFrame = mc.frameLayout(cll = True, label = "Brick")
self.subFrameBrick = mc.radioCollection()
mc.columnLayout()
self.brickType = mc.radioButtonGrp(label = "Style", labelArray3 = ["Solid", "Two Holes", "Three Holes"], numberOfRadioButtons = 3, cal = [1, "center"], onc = "createWall()")
mc.setParent("..")
mc.setParent("..")
# Mansonry Wall framlayout
self.mansonryFrame = mc.frameLayout(collapsable = True, label = "Mansonry Wall")
self.mansonryCheck = mc.radioButtonGrp(label = "", labelArray2 = ["No Arrangement", "Cross Arrangement"], numberOfRadioButtons = 2, cal = [1, "center"])
mc.setParent("..")
# Row and Column
self.wallFrame = mc.frameLayout(collapsable = True, label = "Row & Column")
self.row = mc.intSliderGrp(field=True, label = "Row", minValue = 0, maxValue = 10, fieldMinValue = 0, fieldMaxValue = 9999, value = 0, cal = [1, "center"] )
self.column = mc.intSliderGrp(field=True, label = "Column", minValue = 0, maxValue = 10, fieldMinValue = 0, fieldMaxValue = 9999, value = 0, cal = [1, "center"] )
mc.setParent("..")
self.matFrame = mc.frameLayout(collapsable = True, label = "Brick Material")
self.matButton = mc.button(label = "Assign Material", command = self.assignMatCmd)
self.lightButton = mc.button(label = "Assign Env Light", command = self.assignHdriCmd)
mc.setParent("..")
self.columnValue = mc.intSliderGrp(self.column, query = True, value = True)
self.rowValue = mc.intSliderGrp(self.row, query = True, value = True)
self.pattern = mc.radioButtonGrp(self.brickType, query = True, select = True)
self.mansonryPattern = mc.radioButtonGrp(self.mansonryCheck, query = True, select = True)
def createWall(self, *args):
# grab the value from options
self.columnValue = mc.intSliderGrp(self.column, query = True, value = True)
self.rowValue = mc.intSliderGrp(self.row, query = True, value = True)
self.pattern = mc.radioButtonGrp(self.brickType, query = True, select = True)
self.mansonryPattern = mc.radioButtonGrp(self.mansonryCheck, query = True, select = True)
# Solid bricks and no mansonry
if (self.pattern == 1 and self.mansonryPattern == 1):
for r in range(0, self.rowValue):
mc.polyCube(w = 2, h = 0.5, d = 1, name = "Brick" + str(r))
mc.move((2 * r), 0, 0)
mc.select(all = True)
mc.group(name = "G1")
for c in range(1, self.columnValue):
mc.duplicate(name = "G" + "_" + str(c))
mc.move(0, ((0.5 * c)), 0, "G_" + str(c))
mc.select(all = True)
mc.ungroup()
mc.delete(constructionHistory = True)
# Two holes bricks and no mansonry
elif (self.pattern == 2 and self.mansonryPattern == 1):
cy = mc.polyCylinder(r = 0.5)
mc.select(cy)
mc.move(-1, 0, 0)
cy1 = mc.polyCylinder(r = 0.5)
mc.select(cy1)
mc.move(1, 0, 0)
mc.select(all = True)
mc.polyBoolOp("pCylinder1", "pCylinder2", op = 1)
mc.delete(constructionHistory = True)
mc.polyCube(w = 4, h = 1, d = 2)
mc.polyBoolOp("pCube1", "polySurface1", op = 2)
mc.select("polySurface2")
mc.delete(constructionHistory = True)
mc.rename("polySurface2", "box")
for r in range(0, self.rowValue):
mc.duplicate(name = "Brick" + "_" + str(r))
mc.move((4*r), 0, 0)
mc.delete("box")
mc.select(all = True)
mc.group(name = "G1")
for c in range(1, self.columnValue):
mc.duplicate(name = "G" + "_" + str(c))
mc.move(0, (c), 0, "G_" + str(c))
mc.select(all = True)
mc.ungroup()
mc.delete(constructionHistory = True)
# Three holes bricks and no mansonry
elif (self.pattern == 3 and self.mansonryPattern == 1):
c3 = mc.polyCylinder(r = 0.5)
mc.select(c3)
mc.move(-1.3, 0, 0)
c31 = mc.polyCylinder(r = 0.5)
c32 = mc.polyCylinder(r = 0.5)
mc.select(c32)
mc.move(1.3, 0, 0)
mc.select(all = True)
mc.polyBoolOp("pCylinder1", "pCylinder2", op = 1)
mc.polyBoolOp("pCylinder3", "polySurface1", op = 1)
mc.delete(constructionHistory = True)
mc.polyCube(w = 4, h = 1, d = 2)
mc.polyBoolOp("pCube1", "polySurface2", op = 2)
mc.select("polySurface3")
mc.delete(constructionHistory = True)
mc.rename("polySurface3", "box")
for r in range(0, self.rowValue):
mc.duplicate(name = "Brick" + "_" + str(r))
mc.move((4*r), 0, 0)
mc.delete("box")
mc.select(all = True)
mc.group(name = "G1")
for c in range(1, self.columnValue):
mc.duplicate(name = "G" + "_" + str(c))
mc.move(0, (c), 0, "G_" + str(c))
mc.select(all = True)
mc.ungroup()
# Solid bricks and mansonry
elif (self.pattern == 1 and self.mansonryPattern == 2):
for m in range(0, self.rowValue):
mc.polyCube(w = 2, h = 0.5, d = 1, name = "Brick" + str(m))
mc.move(2 * (m), 0, 0)
mc.select(all = True)
mc.group(name = "G1")
for s in range(1, 2):
mc.duplicate(name = "G" + "_" + str(s))
mc.move(1, (0.5 * s), 0, "G_" + str(s))
mc.select(all = True)
mc.ungroup()
mc.group(name = "G2")
if (self.columnValue % 2 == 0):
for n in range(1, int(abs(self.columnValue*(0.5)))):
mc.duplicate(name = "G" + "_" + str(n))
mc.move(-0.001, n, 0, "G_" + str(n))
mc.select(all = True)
mc.ungroup()
if (self.columnValue % 2 == 1):
for n in range(1, int(abs(self.columnValue * 0.5))+1):
mc.duplicate(name = "G" + "_" + str(n))
mc.move(-0.001, n, 0, "G_" + str(n))
mc.ungroup()
lastLayer = mc.ls(selection = True)
print(lastLayer)
for i in range(self.rowValue, len(lastLayer)):
mc.delete(lastLayer[i])
mc.group(name = "G_")
mc.select(all = True)
mc.ungroup()
# Two holes bricks and mansonry
elif (self.pattern == 2 and self.mansonryPattern == 2):
cy = mc.polyCylinder(r = 0.5)
mc.select(cy)
mc.move(-1.3, 0, 0)
cy1 = mc.polyCylinder(r = 0.5)
mc.select(cy1)
mc.move(1.3, 0, 0)
mc.select(all = True)
mc.polyBoolOp("pCylinder1", "pCylinder2", op = 1)
mc.delete(constructionHistory = True)
mc.polyCube(w = 4, h = 1, d = 2)
mc.polyBoolOp("pCube1", "polySurface1", op = 2)
mc.select("polySurface2")
mc.delete(constructionHistory = True)
mc.rename("polySurface2", "box")
for r in range(0, self.rowValue):
mc.duplicate(name = "Brick" + "_" + str(r))
mc.move((4*r), 0, 0)
mc.delete("box")
mc.select(all = True)
mc.group(name = "G1")
for c in range(1, 2):
mc.duplicate(name = "G" + "_" + str(c))
mc.move(2, (c), 0, "G_" + str(c))
mc.select(all = True)
mc.group(name = "G2")
if (self.columnValue % 2 == 0):
for n in range(1, int(abs(self.columnValue*(0.5)))):
mc.duplicate(name = "Tw" + "_" + str(n))
mc.move(-0.001, 2*n, 0, "Tw_" + str(n))
mc.select(all = True)
mc.ungroup()
mc.ungroup()
if (self.columnValue % 2 == 1):
for n in range(1, int(abs(self.columnValue * 0.5))+1):
mc.duplicate(name = "Tw" + "_" + str(n))
mc.move(-0.001, 2*n, 0, "Tw_" + str(n))
mc.ungroup()
mc.ungroup()
lastLayer = mc.ls(selection = True)
print(lastLayer)
for i in range(self.rowValue, len(lastLayer)):
mc.delete(lastLayer[i])
mc.group(name = "Tw_")
mc.select(all = True)
mc.select("*Tw*", "*G*")
mc.ungroup()
# Three holes bricks and mansonry
elif (self.pattern == 3 and self.mansonryPattern == 2):
c3 = mc.polyCylinder(r = 0.5)
mc.select(c3)
mc.move(-1.3, 0, 0)
c31 = mc.polyCylinder(r = 0.5)
c32 = mc.polyCylinder(r = 0.5)
mc.select(c32)
mc.move(1.3, 0, 0)
mc.select(all = True)
mc.polyBoolOp("pCylinder1", "pCylinder2", op = 1)
mc.polyBoolOp("pCylinder3", "polySurface1", op = 1)
mc.delete(constructionHistory = True)
mc.polyCube(w = 4, h = 1, d = 2)
mc.polyBoolOp("pCube1", "polySurface2", op = 2)
mc.select("polySurface3")
mc.delete(constructionHistory = True)
mc.rename("polySurface3", "box")
for r in range(0, self.rowValue):
mc.duplicate(name = "Brick" + "_" + str(r))
mc.move((4*r), 0, 0)
mc.delete("box")
mc.select(all = True)
mc.group(name = "G1")
for c in range(1, 2):
mc.duplicate(name = "G" + "_" + str(c))
mc.move(2, (c), 0, "G_" + str(c))
mc.select(all = True)
mc.group(name = "G2")
if (self.columnValue % 2 == 0):
for n in range(1, int(abs(self.columnValue*(0.5)))):
mc.duplicate(name = "Tw" + "_" + str(n))
mc.move(-0.001, 2*n, 0, "Tw_" + str(n))
mc.select(all = True)
mc.ungroup()
mc.ungroup()
if (self.columnValue % 2 == 1):
for n in range(1, int(abs(self.columnValue * 0.5))+1):
mc.duplicate(name = "Tw" + "_" + str(n))
mc.move(-0.001, 2*n, 0, "Tw_" + str(n))
mc.ungroup()
mc.ungroup()
lastLayer = mc.ls(selection = True)
print(lastLayer)
for i in range(self.rowValue, len(lastLayer)):
mc.delete(lastLayer[i])
mc.group(name = "Tw_")
mc.select(all = True)
mc.select("*Tw*", "*G*")
mc.ungroup()
# create material for bricks
def assigntMaterial(self, *arg):
# OSL pattern to aiStandardSurface
pattern = mc.shadingNode("aiStandardSurface", asShader = True, name = "Pattern")
mc.setAttr(pattern+".base", 0.364)
mc.setAttr(pattern+".specular", 0.706)
mc.setAttr(pattern+".specularRoughness", 0.2)
# mix shader to constant color and osl noise pattern
oslMix = mc.shadingNode("aiMixShader", asShader = True, name = "oslMix")
mc.connectAttr(oslMix+".outColor",pattern+".baseColor")
renderOslMix = mc.sets(name=oslMix+"SG", empty=True, renderable=True, noSurfaceShader=True)
mc.connectAttr(oslMix+".outColor",renderOslMix+".surfaceShader")
# OSL Pattern
andersonOSL = mc.shadingNode("aiAndersonOSL", asShader = True, name = "Anderson_OSL_Noise")
place2dTextOsl = mc.shadingNode("place2dTexture", asUtility = True)
mc.connectAttr(place2dTextOsl+".outU",andersonOSL+".s")
mc.connectAttr(place2dTextOsl+".outV",andersonOSL+".t")
mc.setAttr(andersonOSL+".f", 0.05)
mc.setAttr(place2dTextOsl+".rotateUV", 109.161)
mc.connectAttr(andersonOSL+".resultRGB",oslMix+".shader1")
# Constant Color and color jitter
color1 = mc.shadingNode("colorConstant", asShader=True, name="color")
Jt = mc.shadingNode("aiColorJitter", asShader=True, name = "jitter")
mc.setAttr(color1+".inColor", 0.642, 0.641, 0.641, type="double3")
mc.setAttr(Jt+".spaceName", "object", type = "string")
mc.setAttr(Jt+".dataHueMin", -1)
mc.setAttr(Jt+".dataHueMax", -0.234)
mc.setAttr(Jt+".dataSeed", 1234)
mc.connectAttr(color1+".outColor", Jt+".input")
mc.connectAttr(Jt+".outColor", oslMix+".shader2")
# connect displacement
displace1 = mc.shadingNode("displacementShader", asShader = True)
OSLsg = mc.sets(name=pattern+"SG", empty=True, renderable=True, noSurfaceShader=True)
mc.connectAttr(andersonOSL+".resultRGBR",displace1+".displacement")
mc.connectAttr(displace1+".displacement",OSLsg+".displacementShader")
mc.connectAttr(pattern+".outColor",OSLsg+".surfaceShader")
# create volume noise for pattern normal
VN = mc.shadingNode("volumeNoise", asShader = True)
VNTexture = mc.shadingNode("place3dTexture", asUtility = True)
VNBump = mc.shadingNode("bump3d", asUtility = True)
mc.connectAttr(VNTexture+".matrix",VN+".placementMatrix")
mc.setAttr(VN+".noiseType", 0)
mc.connectAttr(VN+".outAlpha",VNBump+".bumpValue")
mc.connectAttr(VNBump+".outNormal",pattern+".normalCamera")
# making normal map
normalMap = mc.shadingNode("aiMixShader", asShader = True, name = "normal")
mc.connectAttr(pattern+".outColor",normalMap+".shader1")
mc.connectAttr(VNBump+".outNormal",normalMap+".shader2")
renderNormalMap = mc.sets(name=normalMap+"SG", empty=True, renderable=True, noSurfaceShader=True)
mc.connectAttr(normalMap+".outColor",renderNormalMap+".surfaceShader")
# create render surface for brick
global brickSurface
brickSurface = mc.shadingNode("aiStandardSurface", asShader = True, name = "brickSurface")
mc.setAttr(brickSurface+".baseColor", 0.365, 0.313, 0.306, type="float3")
mc.setAttr(brickSurface+".specular", 0.111)
bumpSurface = mc.shadingNode("bump2d", asShader = True)
mc.connectAttr(normalMap+".outColorR",bumpSurface+".bumpValue")
mc.connectAttr(bumpSurface+".outNormal",brickSurface+".normalCamera")
# Create SkyDome light and assign it HDRI
def assignHdri(self, *arg):
skydome = mc.shadingNode("aiSkyDomeLight", asLight=True)
skyHdri = mc.shadingNode("file", asTexture = True)
place2dText4 = mc.shadingNode("place2dTexture", asUtility = True)
hdriPath = "C:/Users/Anderson/Documents/hdri/garden_nook_4k.exr"
mc.setAttr(skyHdri+".fileTextureName",hdriPath,type = "string")
mc.connectAttr(place2dText4+".outUV", skyHdri+".uvCoord")
mc.connectAttr(place2dText4+".coverage",skyHdri+".coverage")
mc.connectAttr(place2dText4+".translateFrame",skyHdri+".translateFrame")
mc.connectAttr(place2dText4+".rotateFrame",skyHdri+".rotateFrame")
mc.connectAttr(place2dText4+".mirrorU",skyHdri+".mirrorU")
mc.connectAttr(place2dText4+".mirrorV",skyHdri+".mirrorV")
mc.connectAttr(place2dText4+".stagger",skyHdri+".stagger")
mc.connectAttr(place2dText4+".wrapU",skyHdri+".wrapU")
mc.connectAttr(place2dText4+".wrapV",skyHdri+".wrapV")
mc.connectAttr(place2dText4+".repeatUV",skyHdri+".repeatUV")
mc.connectAttr(place2dText4+".offset",skyHdri+".offset")
mc.connectAttr(place2dText4+".rotateUV",skyHdri+".rotateUV")
mc.connectAttr(place2dText4+".noiseUV",skyHdri+".noiseUV")
mc.connectAttr(place2dText4+".vertexUvOne",skyHdri+".vertexUvOne")
mc.connectAttr(place2dText4+".vertexUvTwo",skyHdri+".vertexUvTwo")
mc.connectAttr(place2dText4+".vertexUvThree",skyHdri+".vertexUvThree")
mc.connectAttr(place2dText4+".vertexCameraOne",skyHdri+".vertexCameraOne")
mc.connectAttr(skyHdri+".outColor",skydome+".color")
def finalProjectAndersonYang():
testWindow = OptionsWindow()
testWindow.create()
Comments