#F. Hiba, C. Mendes, B. Lefebvre, P. Hubert | |
import maya.api.OpenMaya as om | |
import maya.cmds as cmds | |
from math import radians, degrees, sin, cos | |
from random import gauss, random, uniform, shuffle | |
from copy import copy | |
#4 utilities function related to transformations | |
#give the quaternion corresponding to the rottion from vector 1 to vector 2 | |
def getQuaternion (vector1=om.MVector (0,0,0), vector2=om.MVector (0,0,0)): | |
dot = vector1^vector2 #get axis | |
dot = dot.normalize() # normalize axis | |
angle = vector1.angle(vector2) #get angle (in rad) | |
quaternion = om.MQuaternion (dot[0]*sin(angle/2),dot[1]*sin(angle/2),dot[2]*sin(angle/2),cos(angle/2)) #convert. to quaternion | |
return quaternion | |
#convert axis + angle to quaternion | |
def toQuaternion (axe=om.MVector (0,1,0), angle=0): | |
angle = radians(angle) | |
quaternion = om.MQuaternion (axe[0]*sin(angle/2),axe[1]*sin(angle/2),axe[2]*sin(angle/2),cos(angle/2)) | |
return quaternion | |
def getMatrix(MVectorx,MVectory,MVectorz,MVectorPos): | |
matrix = om.MMatrix([ | |
MVectorx.x,MVectorx.y,MVectorx.z,0, | |
MVectory.x,MVectory.y,MVectory.z,0, | |
MVectorz.x,MVectorz.y,MVectorz.z,0, | |
MVectorPos.x,MVectorPos.y,MVectorPos.z,1 | |
]) | |
return matrix | |
#return closest point on the mesh and the corresponding normal. Result as [mVector,mVector] | |
def getPointNormal (mMesh, point): | |
point = om.MPoint(point) | |
result = mMesh.getClosestPointAndNormal(point, om.MSpace.kWorld) | |
res0 = om.MVector(result[0]) | |
result = [res0,result[1]] | |
return result | |
def getClosestVertex(mayaMesh,mVector): | |
selectionList = om.MSelectionList() | |
selectionList.add(mayaMesh) | |
dPath = selectionList.getDagPath(0) | |
mMesh = om.MFnMesh(dPath) | |
ID = mMesh.getClosestPoint(om.MPoint(mVector), space=om.MSpace.kWorld)[1] #getting closest face ID | |
list = cmds.ls( cmds.polyListComponentConversion (mayaMesh+'.f['+str(ID)+']',ff=True,tv=True),flatten=True)#face's vertices list | |
#setting vertex [0] as the closest one | |
d = mVector-om.MVector(cmds.xform(list[0], t=True, ws=True, q=True)) | |
smallerDist2 = d.x*d.x+d.y*d.y+d.z*d.z #using distance squared to compare distance | |
closest = list[0] | |
#iterating from vertex [1] | |
for i in range(1,len(list)) : | |
d = mVector-om.MVector(cmds.xform(list[i], t=True, ws=True, q=True)) | |
d2 = d.x*d.x+d.y*d.y+d.z*d.z | |
if d2<smallerDist2: | |
smallerDist2=d2 | |
closest=list[i] | |
return closest | |
def getLastVertex(mayaMesh): | |
list = cmds.ls(cmds.polyListComponentConversion(mayaMesh, tv=True),flatten=True) | |
last = list[-1] | |
return last | |
def getFirstVertex(mayaMesh): | |
list = cmds.ls(cmds.polyListComponentConversion(mayaMesh, tv=True), flatten=True) | |
first = list[0] | |
return first | |
#CLASSES | |
#custom mesh class. Used to point open maya's mMesh object (will be mesh.mMesh) | |
class Mesh: | |
def setmMesh(self, mayamesh): | |
self.mayamesh = mayamesh | |
self.refMesh = cmds.duplicate(self.mayamesh, n='referenceMesh')[0] | |
cmds.parent(self.refMesh, grp.grp) | |
cmds.polySetToFaceNormal(self.refMesh) | |
cmds.hide (self.refMesh) | |
selectionList = om.MSelectionList() | |
selectionList.add(self.refMesh) | |
dPath = selectionList.getDagPath(0) | |
self.mMesh = om.MFnMesh(dPath) | |
def reset(self): | |
self.refMesh = cmds.duplicate(self.mayamesh, n='referenceMesh')[0] | |
cmds.parent(self.refMesh, grp.grp) | |
cmds.polySetToFaceNormal(self.refMesh) | |
cmds.hide (self.refMesh) | |
selectionList = om.MSelectionList() | |
selectionList.add(self.refMesh) | |
dPath = selectionList.getDagPath(0) | |
self.mMesh = om.MFnMesh(dPath) | |
# Add is used to add a copy of a given mesh to the reference. Used to update ref. mesh in real time. | |
def add(self, mayamesh): | |
self.toAdd = cmds.duplicate(mayamesh, n='referenceMesh')[0] | |
self.refMesh = cmds.polyUnite([self.refMesh,self.toAdd], ch=False)[0] | |
cmds.parent(self.refMesh, grp.grp) | |
cmds.hide(self.refMesh) | |
selectionList = om.MSelectionList() | |
selectionList.add(self.refMesh) | |
dPath = selectionList.getDagPath(0) | |
self.mMesh = om.MFnMesh(dPath) | |
#class to point locator | |
class Locator: | |
def setup(self, locator): | |
self.pos = om.MVector(cmds.xform(locator, query=True, t=True, worldSpace=True)) | |
#custom group class. Used to store generated geo. | |
class Grp: | |
def __init__(self): | |
self.grp = cmds.group (em=True, n='generation') | |
def reset (self): | |
if hasattr(self, 'grp'): | |
if cmds.objExists (self.grp): | |
cmds.delete (self.grp) | |
print 'deleted' | |
self.grp = cmds.group (em=True, n='generation') | |
def unite(self): | |
geo = cmds.polyUnite(cmds.listRelatives (self.grp), ch=False, n='Geometry') | |
return geo | |
# custom Vector class. Vectors associate : Vector direction (as openmaya MVector), | |
# Vector position (as om MVector), corresponding normal and point on mesh (both as om MVector), | |
# and corresponding NEXT normal and Point (Normal an Point at vector's extremity). Theses last | |
# parameters are used to calculate next vector's position | |
class Vector: | |
def __init__(self): #creates a standard Vector. On position 0, value 1 z, normal 1 y | |
self.mVector = om.MVector(0, 0, 1)*var.scaleFactor | |
self.pos = om.MVector() | |
self.mNormal = om.MVector(0, 1, 0) | |
def firstSetup(self, locator, mMesh): #set up the first vector : establish his parameters for a given mesh(mMesh custom class) and a given starting postion(locator custom class). | |
self.__init__() #to avoid glitch when used on a allready set up vector | |
self.pos = locator.pos #moves vector to locator's position | |
result= getPointNormal(mMesh, self.pos) #get point an normal on mesh | |
self.mNormal = result[1] | |
self.pos = result[0]+(self.mNormal*var.distance) #move vector next to the surface. Distance controlled by var.distance. | |
quat = getQuaternion (om.MVector(0,1,0), self.mNormal) #get quat. corresponding to the rotation between y axis and the normal on starting point. | |
self.mVector = self.mVector.rotateBy(quat) #uses quad to align vector tangent to the surface. | |
self.mVector = self.mVector.normalize()*var.scaleFactor #scale vector's length by var.scaleFactor | |
result = getPointNormal(mMesh, self.pos+self.mVector) #calculate next point and normal to pass it to the next vector | |
self.pointNext = result[0] | |
self.mNormalNext = result[1] | |
def startRotation (self, mMesh, angle=0.0): #apply a rotation to a given allready set up vector. Use for first vector setup and for bigger angle variation at divisions | |
self.origin = self.mVector | |
self.mVector = self.origin.rotateBy(toQuaternion (self.mNormal, angle)) #modify start angle | |
#redefine other parameters | |
self.mVector = self.mVector.normalize()*var.scaleFactor | |
result = getPointNormal(mMesh, self.pos+self.mVector) | |
self.pointNext = result[0] | |
self.mNormalNext = result[1] | |
# Setup method sets up a vector from a given previous vector. Use to align one vector after the other. | |
def setup (self, mMesh, vector, distance) : #vector must be of type : custom Vector class. | |
self.mVector = vector.mVector #get previous vector's parameter | |
self.pos = vector.pos+vector.mVector #get his new position | |
self.mNormal = vector.mNormalNext #get his normal and point (calculated by previous vector) | |
self.point = vector.pointNext | |
#apply a rotation. Random variation based on gauss distrib. with variance = var.angleAmplitude | |
self.mVector=self.mVector.rotateBy(toQuaternion (self.mNormal, gauss(0, var.angleAmplitude))) | |
#calculate next normal and point | |
result = getPointNormal(mMesh,self.pos+self.mVector) | |
self.pointNext = result[0] | |
self.mNormalNext = result[1] | |
#adjust vector with parameters : next point and next normal. Normalize en rescale to keep a constant length. | |
self.mVector = (self.pointNext + (self.mNormalNext*distance) - self.pos).normalize()*var.scaleFactor | |
#mehtod similar to setup without angle variation and without correction. Used to obtain vector colinear to the previous one. | |
#Used at divisions to avoid extrusion glitch. | |
def setupStraight (self,mMesh, vector, distance) : | |
self.mVector = vector.mVector #get previous vector's parameter | |
self.pos = vector.pos+vector.mVector #get his new position | |
self.mNormal = vector.mNormalNext #get his normal and point (calculated by previous vector) | |
self.point = vector.pointNext | |
result= getPointNormal(mMesh,self.pos+self.mVector) #calculate next normal and point | |
self.pointNext = result[0] | |
self.mNormalNext = result[1] | |
#creates a new point on the bezier curve 'bezier' corresponding to self.pos. Used to store points during generation loop. | |
def display(self, bezier) : | |
cmds.curve (bezier, append=True, p=(self.pos+self.mVector)) | |
#invert vector. Used to create the reverse branch on first iteration (to fill hole) | |
def invert(self): | |
self.copy = copy(self) | |
self.pos = self.copy.pos+self.copy.mVector | |
self.mVector = -(self.copy.mVector) | |
#branch class is used to create vectors un loop and store them as a maya bezier curve. Has a fonction to create geo too. | |
#firstVector and lastVector are first and last vectors of the branch. first vector used for extrusion and last vector used to align next branch. | |
class Branch: | |
#make a branch from a single vector. Used for first vector. | |
def firstSetup(self, vector): | |
self.bezier = cmds.curve (p=vector.pos, d=1) #creates a curve | |
cmds.parent(self.bezier, grp.grp) #parent to grp.grp (custom group class) | |
vector.display(self.bezier) #add point | |
self.firstVector = copy(vector) #fistvector used to set up extrusion in makeGeo | |
self.lastVector = self.firstVector | |
#similar to firstSetup but without creating geo. and inverting first vector. Used to fill hole at strat (expand in both extremities) | |
def firstSetupInv(self, branch): | |
self.firstVector = copy(branch.firstVector) #fistvector used to set up extrusion in makeGeo | |
self.lastVector = self.firstVector | |
self.lastVector.invert() | |
self.geo=branch.geo | |
#Setup a branch for a given previous branch. Store points in a bezier curve. | |
#use vector.setup in a loop | |
#creates 2 vectors before starting the loop : | |
#the first one is a straight one (to avoid extrusion glitch) | |
#the second one gets an extra rotation to make branches expand at division. | |
def setup (self, branch, iterations, close=False): | |
i = 0 | |
self.bezier = cmds.curve(p=branch.lastVector.pos+branch.lastVector.mVector, d=2) #creates curve | |
cmds.parent(self.bezier, grp.grp ) | |
#first vector | |
self.vector0 = Vector() #creates vector0 | |
self.vector0.setupStraight (myMesh.mMesh, branch.lastVector, distance=var.distance)#set up vector0 | |
self.vector0.display(self.bezier) #add point to the curve | |
self.firstVector = copy(self.vector0)#store firstVector in memory | |
#second vector = the one affected by the "rotation at division" factor. | |
self.vector1 = Vector() #creates and sets up a second vector (not straight) | |
self.vector1.setup(myMesh.mMesh, self.vector0, distance=var.distance) | |
self.vector1.startRotation(mMesh=myMesh.mMesh, angle=gauss(0,var.angleAmplitudeDiv)) #rotates it for a bigger offset | |
self.vector1.display(self.bezier) | |
self.vector0 = self.vector1 | |
#loop | |
while i < iterations-2: | |
if close == True : #close is used for last branches. The vector as to be progressively closer to match with the extrusion witch will have a reduced diamater. | |
self.vector1.setup (myMesh.mMesh,self.vector0,distance=var.distance*(1-i/(iterations-2)) ) | |
else: | |
self.vector1.setup (myMesh.mMesh,self.vector0,distance=var.distance) | |
leaves.append(copy(self.vector1)) | |
self.vector1.display(self.bezier) | |
self.vector0=self.vector1 | |
i = i + 1 | |
self.lastVector = self.vector1 #point last vector | |
#makeGeo extrude the curve. Close argument used for extremities. | |
def makeGeo(self, branch=0, close=False, inv=False, first=False): | |
if first == True : | |
z = self.firstVector.mVector | |
y = self.firstVector.mNormal | |
x = (y^z).normal() | |
y = z^x | |
mat1 = getMatrix(x.normal(), y.normal(), z.normal(), self.firstVector.pos) | |
sliceTest = cmds.circle(r=var.radius, s=8)[0]#creating the "slice" circle for extrusion | |
cmds.xform (sliceTest, matrix=mat1) #rotating and moving it to match curve | |
else : | |
#vertex = om.MVector(cmds.xform (getClosestVertex (branch.geo,self.firstVector.pos),t=True,ws=True,q=True)) | |
if inv == True: | |
vertex = om.MVector(cmds.xform(getFirstVertex(branch.geo), t=True, ws=True, q=True)) | |
else : | |
vertex = om.MVector(cmds.xform(getLastVertex(branch.geo), t=True, ws=True, q=True)) | |
x = (vertex-self.firstVector.pos).normal() | |
z = self.firstVector.mVector.normal() | |
y = z^x | |
#cmds.curve(p=[self.firstVector.pos,self.firstVector.pos+x],d=1) | |
mat1 = getMatrix(x.normal(), y.normal(), z.normal(), self.firstVector.pos) | |
sliceTest = cmds.circle(r=var.radius, s=8)[0]#creating the "slice" circle for extrusion | |
cmds.xform (sliceTest, matrix=mat1) #rotating and moving it to match curve | |
if close == True: | |
self.nurbs=cmds.extrude (sliceTest, self.bezier, po=0, upn=False, scale=0)[0] #[0] : extrude returns a list | |
else : | |
self.nurbs=cmds.extrude (sliceTest, self.bezier, po=0, upn=False, scale=1)[0] | |
self.geo = cmds.nurbsToPoly(self.nurbs, f=3, pt=1, mnd=True)[0] | |
cmds.delete(self.nurbs) | |
cmds.parent(self.geo, grp.grp) | |
cmds.delete(self.bezier) | |
cmds.delete(sliceTest) | |
return self.geo | |
#Branchlist class is used to makes several branches. It stores the list of the last branches and | |
# uses this list to creates new branches at their extremities | |
class Branchlist: | |
def __init__(self): | |
self.list = [] | |
self.listClose = [] | |
def initialize(self,branch,branchInv): | |
self.list = [branch] | |
self.branchInv = branchInv | |
#print self.branchInv.lastVector.mVector | |
#print self.list[0].lastVector.mVector | |
def iterate(self, number=1): | |
#Check if this is the first iteration. If yes, sets up an extra branch to fill hole. | |
if var.firstIteration == True : | |
print 'first' | |
var.firstIteration = False | |
branchI = Branch() | |
branchI.setup(branch = self.branchInv, iterations=var.iterations + (var.iterations-1)*var.iterationsVar*uniform(-1,1), close=True) | |
geo=copy(branchI.makeGeo(branch=self.branchInv, first=False, inv=True, close=True)) | |
myMesh.add(geo) | |
cmds.refresh() | |
#Next block is the loop generation algorithm | |
n = 0 | |
while n<number: | |
n = n+1 | |
self.listClose = [] | |
shuffle(self.list) | |
self.templist = [] | |
self.delIndex = [] | |
if len(self.list) > 1: | |
for i in range(1, len(self.list)): | |
if random() < var.closeRate : | |
self.listClose.append(copy(self.list[i])) | |
self.delIndex.append(i) | |
if len(self.delIndex) > 0: | |
for i in sorted (self.delIndex, reverse=True) : | |
del (self.list[i]) | |
for branch in self.list: | |
branch1 = Branch() | |
branch1.setup(branch=branch,iterations = var.iterations+(var.iterations-1)*var.iterationsVar*uniform(-1,1)) | |
geo1 = copy(branch1.makeGeo(branch=branch)) | |
self.templist.append(branch1) | |
cmds.refresh() | |
if random() < var.divRate: | |
branch2 = Branch() | |
branch2.setup(branch = branch,iterations=var.iterations+(var.iterations-1)*var.iterationsVar*uniform(-1,1)) | |
geo2 = copy(branch2.makeGeo(branch=branch)) | |
myMesh.add(geo2) | |
self.templist.append(branch2) | |
cmds.refresh() | |
myMesh.add(geo1) | |
self.list = copy(self.templist) | |
for branch in self.listClose: | |
branch1 = Branch() | |
branch1.setup(branch=branch,iterations=var.iterations+(var.iterations-1)*var.iterationsVar*uniform(-1,1),close=True) | |
geo = copy(branch1.makeGeo(branch=branch, close=True)) | |
myMesh.add(geo) | |
cmds.refresh() | |
def close(self): | |
for branch in self.list: | |
branch1 = Branch() | |
branch1.setup(branch=branch,iterations=var.iterations+(var.iterations-1)*var.iterationsVar*uniform(-1,1),close=True) | |
geo = copy(branch1.makeGeo(branch=branch, close=True)) | |
myMesh.add(geo) | |
cmds.refresh() | |
#variables class. Is used to create a single object representing all the variables. | |
class Var: | |
def __init__(self): | |
self.scaleFactor = 1 | |
self.radius=0.5 | |
self.penetration = 0 | |
self.distance=self.radius*(1-self.penetration) | |
self.startAngle = 0 | |
self.angleAmplitude = 30 | |
self.angleAmplitudeDiv = 45 | |
self.iterations = 50 | |
self.iterationsVar = 30 | |
self.divRate = 0.3 | |
self.closeRate = 0.2 | |
self.loopNumber = 1 | |
self.leafScaleVar = 0.2 | |
self.leafRot1Var = 15 | |
self.leavesDensity = 0.4 | |
self.firstIteration = True | |
#updates value based on sliders. Called by slider. | |
def update(self): | |
self.scaleFactor = cmds.floatSliderGrp(scaleFactor, v=True, query = True) | |
self.radius = cmds.floatSliderGrp(radius, v=True, query = True) | |
self.penetration = cmds.floatSliderGrp(penetration, v=True, query = True) | |
self.distance = self.radius*(1-self.penetration) | |
self.startAngle = cmds.intSliderGrp(startAngle, v=True, query = True) | |
self.angleAmplitude = cmds.floatSliderGrp(angleAmplitude, v=True, query = True) | |
self.angleAmplitudeDiv = cmds.floatSliderGrp(angleAmplitudeDiv, v=True, query = True) | |
self.iterations = cmds.intSliderGrp(iterations, v=True, query = True) | |
self.iterationsVar = cmds.floatSliderGrp(iterationsVar, v=True, query = True) | |
self.divRate = cmds.floatSliderGrp(divRate, v=True, query = True) | |
self.closeRate = cmds.floatSliderGrp(closeRate, v=True, query = True) | |
self.loopNumber = cmds.intSliderGrp(loopNumber, v=True, query=True) | |
self.leavesScaleVar = cmds.floatSliderGrp(leavesScaleVar, v=True, query=True) | |
self.leavesRot1Var = cmds.intSliderGrp(leavesRot1Var, v=True, query=True) | |
self.leavesDensity = cmds.floatSliderGrp(leavesDensity, v=True, query=True) | |
self.leavesPlacementIntMin = cmds.intSliderGrp (leavesPlacementIntMin, v=True, query=True) | |
self.leavesPlacementIntMax = cmds.intSliderGrp (leavesPlacementIntMax, v=True, query=True) | |
#leaves class is used to store a vector list and to place leaves along these vectors. | |
class Leaves: | |
def __init__(self): | |
self.vectorList = [] | |
def setMesh(self, mayamesh): | |
cmds.xform(mayamesh, piv=(0,0,0)) | |
cmds.makeIdentity (mayamesh, apply=True) | |
self.mesh = mayamesh | |
def append (self,vector): | |
self.vectorList.append(vector) | |
def reset (self): | |
if hasattr(self,'grp'): | |
if cmds.objExists (self.grp): | |
print 'del' | |
cmds.delete (self.grp) | |
self.grp = cmds.group (em=True, n='Leaves') | |
'''cmds.parent (self.grp,grp.grp)''' | |
def placeGeo(self): | |
for vector in self.vectorList: | |
if random() < var.leavesDensity: | |
z = vector.mVector.normal() | |
x = vector.mNormal | |
y = z^x | |
x = y^z | |
pos = vector.pos | |
mat1 = getMatrix(x, y, z, pos) | |
mat2 = getMatrix(x, -y, z, pos) | |
leaf = cmds.duplicate (self.mesh) | |
loc = cmds.spaceLocator () | |
cmds.parent(leaf,loc) | |
if uniform (0, 1)>0.5: | |
cmds.xform (loc, matrix=mat1 )#place on the branch | |
else: | |
cmds.xform (loc, matrix=mat2 )#place on the branch | |
cmds.move(0.9*var.radius,0,0,leaf,os=True,r=True)#place on the surface | |
placementAngle = uniform (var.leavesPlacementIntMin,var.leavesPlacementIntMax) | |
cmds.rotate (0, 0, -placementAngle, loc, os=True, r=True, fo=True)#place at various angle around Z. Zero angle = leaf orientation follows normal. | |
cmds.rotate(gauss(0, var.leavesRot1Var),gauss(0, var.leavesRot1Var),0, leaf, os=True, r=True, fo=True) #random rotation | |
s=gauss(1, var.leavesScaleVar) | |
cmds.scale(s, s, s, leaf, r=True) | |
cmds.parent(leaf,world=True) | |
cmds.parent(leaf,self.grp) | |
cmds.delete (loc) | |
cmds.refresh() | |
#FONCTIONS CALL BY BUTTONS | |
#update vector with new variables and set it relative to locator. Use to tweak the first vector's parameters or to display it after reset. | |
def updateVector (vector): | |
var.update() | |
vector.firstSetup(locator=myLocator, mMesh=myMesh.mMesh) | |
vector.startRotation(mMesh=myMesh.mMesh, angle=var.startAngle) | |
if hasattr(branch0, 'bezier'): | |
if cmds.objExists (branch0.bezier): | |
cmds.delete (branch0.bezier) | |
branch0.firstSetup(vector0) | |
if hasattr(branch0, 'geo'): | |
if cmds.objExists (branch0.geo): | |
cmds.delete (branch0.geo) | |
branch0.makeGeo(first=True) | |
branchInv.firstSetupInv(branch0) | |
branchlist.initialize(branch0,branchInv)#put first branch in branchlist | |
def reset(): | |
grp.reset() | |
myMesh.reset() | |
leaves.reset() | |
vector0.__init__() | |
branch0 = Branch() | |
branchInv = Branch() | |
branchlist = Branchlist() | |
updateVector(vector0) | |
leaves.vectorList = [] | |
def resetButton(): | |
var.firstIteration = True | |
try: | |
reset() | |
except: | |
print 'Nothing to reset !' | |
def setLocatorButton (): #launched when setlocator button is pressed. | |
myLocator.setup (cmds.ls(sl=True)[0]) #point locator via custom locator class | |
updateVector(vector0) #update to display vector | |
cmds.viewFit(cmds.ls(sl=True)[0]) | |
def setMeshButton(): | |
try: | |
mesh = cmds.ls(sl=True)[0] | |
grp.reset() | |
myMesh.setmMesh(mesh) | |
except : | |
print 'please select a mesh object !' | |
def iterateButton(): | |
try: | |
if var.firstIteration == True : | |
reset() | |
branchlist.iterate(var.loopNumber) | |
except: | |
print 'set mesh and location first !' | |
def finishButton(): | |
try: | |
branchlist.close() | |
cmds.delete(myMesh.refMesh) | |
except: | |
print 'No generation to terminate !' | |
def combineGeo(): | |
try: | |
geo = grp.unite() | |
grp.reset() | |
var.firstIteration=True | |
cmds.select(geo) | |
except: | |
print 'No geo to combine !' | |
def setLeafButton(): | |
leaves.setMesh(cmds.ls(sl=True)[0]) | |
def placeLeavesButton(): | |
leaves.reset() | |
leaves.placeGeo() | |
#Objects created at launch to easy variable access. | |
var = Var() | |
myMesh = Mesh() | |
vector0 = Vector() | |
myLocator = Locator() | |
branch0 = Branch() | |
branchInv = Branch() | |
branchlist = Branchlist() | |
grp = Grp() | |
leaves = Leaves() | |
#WINDOW | |
if (cmds.window('RootsAndIvy', exists=True)): | |
cmds.deleteUI('RootsAndIvy', window=True ) | |
myWindow = cmds.window('RootsAndIvy', iconName='mw' ) | |
if (cmds.windowPref( myWindow, exists=True )): | |
cmds.windowPref( myWindow, remove=True ) | |
#MENU | |
menuBarLayout = cmds.menuBarLayout() | |
cmds.menu(label='File') | |
cmds.menuItem(label='Reset') | |
cmds.menu(label='Help', helpMenu=True) | |
cmds.menuItem( label='About...') | |
#MAIN LAYOUT | |
mainColumn = cmds.columnLayout(adjustableColumn=True, w=500, co=['both',5]) | |
#FRAME 1 | |
frame1 = cmds.frameLayout (label='Initial setup', mw=5, parent=mainColumn) | |
f1r = cmds.rowLayout(numberOfColumns=2, parent=frame1, rat=[1,'top',0], adj=2) | |
frame11 = cmds.frameLayout (label='Mesh setup', mw=5, mh=2, parent=f1r, bgs=True) | |
frame12 = cmds.frameLayout (label='Standard division setup', mw=5, parent=f1r, bgs=True) | |
cmds.rowColumnLayout(numberOfColumns=2, parent=frame11, rs=[[2,5]], cs=[2,5], cal=[1,'right']) | |
cmds.text(label='Reference Mesh') | |
cmds.button( label='Set mesh', command=('setMeshButton()')) | |
cmds.text(label='') | |
cmds.button(label='Create locator', command=("cmds.spaceLocator(n='StartLocator')") ) | |
cmds.text(label='Start position') | |
cmds.button(label='Set position', command=('setLocatorButton()') ) | |
cmds.rowLayout(numberOfColumns=2, parent=frame12) | |
#FRAME 2 | |
frame2 = cmds.frameLayout (label=" Generation's setup",mw=5,parent=mainColumn,bgs=True) | |
scaleFactor = cmds.floatSliderGrp(label = 'Scale factor (length)\n Affects precision',field=True,minValue=0.0, maxValue=0.5, fieldMinValue=0.0, fieldMaxValue=5, step=0.01, value=0.1,columnWidth3 = (100,50,50), columnAlign3=('right','center','center'),cc = 'updateVector(vector0)',dc= 'updateVector(vector0)',parent=frame12,ad3=1 ) | |
startAngle = cmds.intSliderGrp(label = 'Angle',field=True,minValue=0.0, maxValue=360, fieldMinValue=0.0, fieldMaxValue=360, step=1, value=0,columnWidth3 = (100,50,50), columnAlign3=('right','center','center'),cc = 'updateVector(vector0)',dc= 'updateVector(vector0)' ,parent=frame12,ad3=1) | |
radius = cmds.floatSliderGrp(label = 'Radius',field=True,minValue=0.0, maxValue=0.5, fieldMinValue=0.0, fieldMaxValue=2, step=0.05, value=0.05,columnWidth3 = (100,50,50), columnAlign3=('right','center','center'),cc = 'updateVector(vector0)',dc= 'updateVector(vector0)',parent=frame12,ad3=1 ) | |
penetration = cmds.floatSliderGrp(label = 'Penetration',field=True,minValue=0.0, maxValue=1, fieldMinValue=0.0, fieldMaxValue=1, step=0.05, value=0.05,columnWidth3 = (100,50,50), columnAlign3=('right','center','center'),cc = 'updateVector(vector0)',dc= 'updateVector(vector0)',parent=frame12,ad3=1 ) | |
iterations = cmds.intSliderGrp(label='Branches average length', field=True,minValue=1, maxValue=100, fieldMinValue=1, fieldMaxValue=100, step=1, value=20,columnWidth3 = (100,50,50), columnAlign3=('right','center','center'),cc = 'var.update()',dc= 'var.update()' ,ad3=1,co3=[0,0,30],ct3=('right','center','right')) | |
iterationsVar = cmds.floatSliderGrp(label='Length variation amplitude', field=True,minValue=0, maxValue=1, fieldMinValue=0, fieldMaxValue=1, step=0.05, value=0.3,columnWidth3 = (100,50,50), columnAlign3=('right','center','center'),cc = 'var.update()',dc= 'var.update()',ad3=1,co3=[0,0,30],ct3=('right','center','right') ) | |
angleAmplitude = cmds.floatSliderGrp(label='Angle randomness variance',field=True,minValue=0.0, maxValue=45, fieldMinValue=0.0, fieldMaxValue=90, step=1, value=15,columnWidth3 = (100,50,50),columnAlign3=('right','center','center'),cc = 'var.update()',dc= 'var.update()',ad3=1,co3=[0,0,30],ct3=('right','center','right')) | |
angleAmplitudeDiv = cmds.floatSliderGrp(label='Angle randomness variance\nat divisions',field=True,minValue=0.0, maxValue=45, fieldMinValue=0.0, fieldMaxValue=90, step=1, value=25,columnWidth3 = (100,50,50), columnAlign3=('right','center','center'),cc = 'var.update()',dc= 'var.update()',ad3=1,co3=[0,0,30],ct3=('right','center','right')) | |
divRate = cmds.floatSliderGrp(label='Divisions rate', field=True, minValue=0, maxValue=1, fieldMinValue=0, fieldMaxValue=1, step=0.01, value=0.30,columnWidth3 = (100,50,50), columnAlign3=('right','center','center'),cc = 'var.update()',dc= 'var.update()' ,ad3=1,co3=[0,0,30],ct3=('right','center','right')) | |
closeRate = cmds.floatSliderGrp(label='Closing rate', field=True, minValue=0, maxValue=1, fieldMinValue=0, fieldMaxValue=1, step=0.01, value=0.20,columnWidth3 = (100,50,50), columnAlign3=('right','center','center'),cc = 'var.update()',dc= 'var.update()',ad3=1,co3=[0,0,30],ct3=('right','center','right') ) | |
cmds.text (label='A high (division rate/closing rate) ratio will lead to exponential generation !') | |
#FRAME 3 | |
frame3 = cmds.frameLayout(label='Create geometry',mw=5,parent=mainColumn) | |
frame31 = cmds.frameLayout(label='Main structure',mw=5,parent=frame3,bgs=True) | |
#ROWCOLUMN with 2 column | |
f31r = cmds.rowColumnLayout(numberOfColumns=2,rs=[[2,5]],cs=[2,5],cal=[1,'right']) | |
#1 | |
loopNumber = cmds.intSliderGrp(label = 'Number of \n iteration\'s \nloop',field=True,minValue=1, maxValue=10, fieldMinValue=1, fieldMaxValue=50, step=1, value=1,columnWidth3 = (100,50,50), ad3=1,columnAlign3=('right','center','center'),cc = 'var.update()',dc= 'var.update()', parent=f31r ) | |
#2 | |
cmds.button(label='Iterate', command=('iterateButton()')) | |
#3 | |
cmds.rowLayout(numberOfColumns=2,parent=f31r) | |
cmds.text (label='Reset current generation') | |
cmds.button( label='reset', command=('resetButton()') ) | |
#4 | |
cmds.rowLayout(numberOfColumns=2, parent=f31r) | |
cmds.text(label='Terminate\n (Plug extremities)') | |
cmds.button(label='Terminate', command=('finishButton()') ) | |
#FRAME4 | |
frame32 = cmds.frameLayout(label='Leaves',mw=5,parent=frame3,bgs=True) | |
cmds.text(label='Leaf primitive orientation must be : \n UP = global Y, leaf direction = global Z, stem basis = 0,0,0') | |
f32r=cmds.rowLayout(numberOfColumns=2,ad2=2,ct2=('right','right')) | |
f32rc=cmds.columnLayout() | |
f32rr=cmds.rowLayout(numberOfColumns=2) | |
cmds.text (label = 'Set leave primitive',parent=f32rr) | |
cmds.button( label='Set Mesh', command=('setLeafButton()'),parent=f32rr) | |
leavesDensity= cmds.floatSliderGrp(label = 'Density',field=True,minValue=0, maxValue=1, fieldMinValue=0, fieldMaxValue=1, step=0.01, value=0.4,columnWidth3 = (100,50,50), columnAlign3=('right','center','center'),cc = 'var.update()',dc= 'var.update()',ad3=1,co3=[0,0,30],ct3=('right','center','right'),parent=f32rc ) | |
leavesScaleVar= cmds.floatSliderGrp(label = 'Scale\n variance',field=True,minValue=0, maxValue=1, fieldMinValue=0, fieldMaxValue=1, step=0.01, value=0.3,columnWidth3 = (100,50,50), columnAlign3=('right','center','center'),cc = 'var.update()',dc= 'var.update()',ad3=1,co3=[0,0,30],ct3=('right','center','right'),parent=f32rc ) | |
leavesRot1Var= cmds.intSliderGrp(label = 'Rotation\n variance',field=True,minValue=0, maxValue=30, fieldMinValue=0, fieldMaxValue=45, step=1, value=15,columnWidth3 = (100,50,50), columnAlign3=('right','center','center'),cc = 'var.update()',dc= 'var.update()',ad3=1,co3=[0,0,30],ct3=('right','center','right'),parent=f32rc ) | |
cmds.columnLayout (parent=f32r) | |
cmds.text (label = 'Positionning interval (0 = normal, 90 = lateral segment) : ') | |
leavesPlacementIntMin = cmds.intSliderGrp(label = 'min',field=True,minValue=0, maxValue=90, fieldMinValue=0, fieldMaxValue=90, step=1, value=45,columnWidth3 = (100,50,50), columnAlign3=('right','center','center'),cc = 'var.update()',dc= 'var.update()',ad3=1,co3=[0,0,30],ct3=('right','center','right')) | |
leavesPlacementIntMax = cmds.intSliderGrp(label = 'max',field=True,minValue=0, maxValue=90, fieldMinValue=0, fieldMaxValue=90, step=1, value=85,columnWidth3 = (100,50,50), columnAlign3=('right','center','center'),cc = 'var.update()',dc= 'var.update()',ad3=1,co3=[0,0,30],ct3=('right','center','right') ) | |
cmds.rowColumnLayout(numberOfColumns=2,rs=[[2,5]],cs=[2,5],cal=[1,'right']) | |
cmds.button(label='Place leaves', command=('placeLeavesButton()')) | |
cmds.button(label='Reset', command=('leaves.reset()')) | |
#FRAME 5 | |
frame5=cmds.frameLayout (label='Utility', mw=5, parent=mainColumn, bgs=True) | |
cmds.rowLayout(numberOfColumns=2) | |
cmds.text (label='Combine geometry') | |
cmds.button(label='Combine geo', command=('combineGeo()') ) | |
var.update() | |
cmds.showWindow('RootsAndIvy') |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment