grow.py 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. # This code is called when instances of this SOP cook.
  2. #scaleFactor = node.parm("scaleFactor").eval()
  3. import random
  4. import os
  5. import sys
  6. from os import path
  7. node = hou.pwd()
  8. geo = node.geometry()
  9. # map user input
  10. GENERATIONS = hou.ch('generations')
  11. TROPISM = hou.Vector3(hou.parmTuple("tropDir").eval())
  12. TROPISMFACTOR = hou.ch('tropStrength')
  13. GRAVITYFACTOR = hou.ch('gFactor')
  14. SEED=123
  15. #hou.ch("falloff")
  16. # the "seed"
  17. # this is the first point from which the tree will grow.
  18. point=geo.createPoint()
  19. pointers=[point]
  20. # init the point attributes to store the values in, as these must exist.
  21. # As we start, the first point's normal will point upward to grow in that direction.
  22. geo.addAttrib(hou.attribType.Point, "N", hou.Vector3([0,1,0]))
  23. geo.addAttrib(hou.attribType.Point, "Branch", 0)
  24. geo.addAttrib(hou.attribType.Point, "Generation", 0)
  25. geo.addAttrib(hou.attribType.Point, "BranchPoint", 0)
  26. geo.addAttrib(hou.attribType.Point, 'NormalizedPosition', 0.0)
  27. geo.addAttrib(hou.attribType.Point, 'Diameter', 1.0)
  28. # Just in case, if we need groups, this is how to do it.
  29. #defaultGrp=geo.createPointGroup('name')
  30. #defaultGrp.add(point)
  31. def duplicatePoint(p):
  32. # hmm. maybe there is sth like this already, but i missed it...
  33. # this duplicates a point and all it's attributes.
  34. dupe=geo.createPoint()
  35. dupe.setPosition(p.position())
  36. allAttr=geo.pointAttribs()
  37. for i in allAttr:
  38. holder=None
  39. holder=p.attribValue(i)
  40. dupe.setAttribValue(i, holder)
  41. return dupe
  42. def tropism(pt,vector,factor):
  43. '''Move point towards another point, as in growing towards a light source (sun)
  44. or a food source (roots)
  45. '''
  46. P = pt.position()
  47. vector=vector.normalized()
  48. pt.setPosition( P + (1*factor) * vector)
  49. def moveAlongNormal(pt,factor):
  50. P = pt.position()
  51. #P = hou.Vector3(P)
  52. # be careful - this returns a value you cant simply add to P, must convert it to a hou.Vector3
  53. N = pt.attribValue("N")
  54. N = hou.Vector3(N)
  55. pt.setPosition( P + (1*factor) * N )
  56. def gravity(pt,factor):
  57. '''Convenience function to move down a point, multiplied by diameter.'''
  58. P = pt.position()
  59. # The downforce D
  60. D = (1 - pt.attribValue('Diameter'))*factor
  61. G = hou.Vector3([0,D,0])
  62. pt.setPosition( P - G)
  63. def createRandomVector():
  64. rx = (random.random()-0.5)*2
  65. ry = (random.random()-0.5)*2
  66. rz = (random.random()-0.5)*2
  67. return hou.Vector3([rx,ry,rz])
  68. def addNoiseToNormal(pt,factor):
  69. N = pt.attribValue("N")
  70. N = hou.Vector3(N)
  71. rx = (random.random()-0.5)*2
  72. ry = (random.random()-0.5)*2
  73. rz = (random.random()-0.5)*2
  74. rvec = hou.Vector3([rx*factor,ry*factor,rz*factor])
  75. newN = rvec + 1 * N
  76. pt.setAttribValue("N", newN)
  77. return pt
  78. def rotateNormal(pt,factor,seed):
  79. N = pt.attribValue("N")
  80. N = hou.Vector3(N)
  81. x = ( random.random()-0.5 )*2
  82. y = ( random.random()-0.5 )*2
  83. z = ( random.random()-0.5 )*2
  84. print 'p random x {0} y {1} z {2}'.format(x,y,z)
  85. x = ( hou.hmath.rand(seed+12*43)-0.5 )*2
  86. y = ( hou.hmath.rand(seed+2+2*3)-0.5 )*2
  87. z = ( hou.hmath.rand(seed+33*2)-0.5 )*2
  88. print 'h random x {0} y {1} z {2}'.format(x,y,z)
  89. rvec = hou.Vector3([x*factor,y*factor,z*factor])
  90. newN = rvec + 1 * N
  91. pt.setAttribValue("N", newN)
  92. return pt
  93. def getPointsByAttribValue(attr,val):
  94. plist=[]
  95. for p in geo.iterPoints():
  96. if p.attribValue(attr)==val:
  97. plist.append(p)
  98. return plist
  99. def getHighestNumericAttrVal(points,attr):
  100. val=0.0
  101. for p in points:
  102. v=float(p.attribValue(attr))
  103. if v > val:val=v
  104. return val
  105. branchNum=0
  106. def step(pointers,stepseed):
  107. ''' step through all head pointers (aka growing branches)'''
  108. updatedPointers=[]
  109. for p in pointers:
  110. #print 'pointer is point w/ # ' + str(p.number())
  111. newP=duplicatePoint(p)
  112. #print 'copy is point w/ # ' + str(newP.number())
  113. updatedPointers.append(newP)
  114. newP.setAttribValue('BranchPoint',newP.attribValue('BranchPoint')+1)
  115. # Should we branch?
  116. # TODO allow multiple branches at once
  117. rnd=random.random()
  118. rnd=hou.hmath.rand(stepseed+11)
  119. if rnd > 0.7:
  120. # Dirty, find a better way soon!
  121. global branchNum
  122. branchNum+=1
  123. global treeInfo
  124. #treeInfo['totalbranches']+=1
  125. #print 'branch'
  126. branchP=duplicatePoint(p)
  127. branchP.setAttribValue('BranchPoint',0)
  128. b=branchP.attribValue('Branch')
  129. branchP.setAttribValue('Branch',branchNum)
  130. #branchP.setAttribValue('Branch',b+1)
  131. g=branchP.attribValue('Generation')
  132. branchP.setAttribValue('Generation',g+1)
  133. updatedPointers.append(branchP)
  134. stepseed+=1
  135. newP=rotateNormal(newP,0.4,stepseed)
  136. # now move along normal
  137. moveAlongNormal(newP,1)
  138. v=hou.Vector3(0,1,1)
  139. tropism(newP,TROPISM,TROPISMFACTOR)
  140. gravity(newP,GRAVITYFACTOR)
  141. newP.setAttribValue('Diameter',newP.attribValue('Diameter')*0.7 )
  142. stepseed+=1
  143. print 'seed ' + str(stepseed)
  144. return updatedPointers
  145. x=step(pointers,1)
  146. seed=2
  147. for arsch in range(GENERATIONS):
  148. #seed=1
  149. x = step(x,seed)
  150. seed+=1
  151. # until there is a better way, 'postprocess' the tree to gain info that is not present at runtime.
  152. def postprocess():
  153. tInfo={}
  154. '''
  155. for n in range(0,branchNum):
  156. for p in geo.iterPoints():
  157. #print p
  158. print p.attribValue('BranchPoint')
  159. #if p.attribValue('Branch') == n:
  160. # print p.attribValue('BranchPoint')
  161. '''
  162. for p in geo.iterPoints():
  163. curBranch=p.attribValue('Branch')
  164. tInfo['branch']=curBranch
  165. print '[[[[[[[[[[***]]]]]]]]]]'
  166. totalBranches=int(getHighestNumericAttrVal(geo.iterPoints(),'Branch'))
  167. #print geo.points()
  168. for i in range(0,totalBranches):
  169. # go through all branches
  170. max=0
  171. for p in getPointsByAttribValue('Branch',i):
  172. v=p.attribValue('BranchPoint')
  173. if v>max: max=v
  174. for p in getPointsByAttribValue('Branch',i):
  175. bp=p.attribValue('BranchPoint')
  176. #print max
  177. #print bp
  178. try:
  179. relpos=float(bp)/float(max)
  180. except:
  181. relpos=0
  182. #p.setAttribValue('NormalizedPosition',1)
  183. p.setAttribValue('NormalizedPosition', float(relpos))
  184. #print '---'
  185. #print bp + max
  186. #print 'branch {0} points {1}'.format(i,max)
  187. # TODO and thoughts
  188. # in addition to the branch 'id' we need a distance of each point of a branch towards it's parent.
  189. # useful: how many 'levels' are we away from the main parent? > point needs to have parent id, increment on branch
  190. # roots
  191. # DIAMETER should decrease by a step after each branch