TESSELLATE_NUMPY.PY 166KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280
  1. # ##### BEGIN GPL LICENSE BLOCK #####
  2. #
  3. # This program is free software; you can redistribute it and/or
  4. # modify it under the terms of the GNU General Public License
  5. # as published by the Free Software Foundation; either version 2
  6. # of the License, or (at your option) any later version.
  7. #
  8. # This program is distributed in the hope that it will be useful,
  9. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. # GNU General Public License for more details.
  12. #
  13. # You should have received a copy of the GNU General Public License
  14. # along with this program; if not, write to the Free Software Foundation,
  15. # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  16. #
  17. # ##### END GPL LICENSE BLOCK #####
  18. # ---------------------------- ADAPTIVE DUPLIFACES --------------------------- #
  19. # ------------------------------- version 0.84 ------------------------------- #
  20. # #
  21. # Creates duplicates of selected mesh to active morphing the shape according #
  22. # to target faces. #
  23. # #
  24. # (c) Alessandro Zomparelli #
  25. # (2017) #
  26. # #
  27. # http://www.co-de-it.com/ #
  28. # #
  29. # ############################################################################ #
  30. import bpy
  31. from bpy.types import (
  32. Operator,
  33. Panel,
  34. PropertyGroup,
  35. )
  36. from bpy.props import (
  37. BoolProperty,
  38. EnumProperty,
  39. FloatProperty,
  40. IntProperty,
  41. StringProperty,
  42. PointerProperty
  43. )
  44. from mathutils import Vector
  45. import numpy as np
  46. from math import *
  47. import random, time, copy
  48. import bmesh
  49. from .utils import *
  50. def anim_tessellate_active(self, context):
  51. ob = context.object
  52. props = ob.tissue_tessellate
  53. if not (props.bool_lock or props.bool_hold):
  54. try:
  55. props.generator.name
  56. props.component.name
  57. bpy.ops.object.tissue_update_tessellate()
  58. except: pass
  59. def anim_tessellate_object(ob):
  60. try:
  61. #bpy.context.view_layer.objects.active = ob
  62. bpy.ops.object.tissue_update_tessellate()
  63. except:
  64. return None
  65. #from bpy.app.handlers import persistent
  66. def anim_tessellate(scene):
  67. try:
  68. active_object = bpy.context.object
  69. old_mode = bpy.context.object.mode
  70. selected_objects = bpy.context.selected_objects
  71. except: active_object = old_mode = selected_objects = None
  72. if old_mode in ('OBJECT', 'PAINT_WEIGHT'):
  73. update_objects = []
  74. for ob in scene.objects:
  75. if ob.tissue_tessellate.bool_run and not ob.tissue_tessellate.bool_lock:
  76. if ob not in update_objects: update_objects.append(ob)
  77. update_objects = list(reversed(update_dependencies(ob, update_objects)))
  78. for ob in update_objects:
  79. override = {'object': ob}
  80. '''
  81. win = bpy.data.window_managers[0].windows[0]#bpy.context.window
  82. scr = win.screen
  83. areas3d = [area for area in scr.areas if area.type == 'VIEW_3D']
  84. region = [region for region in areas3d[0].regions if region.type == 'WINDOW']
  85. override = {
  86. 'window':win,
  87. 'screen':scr,
  88. 'area' :areas3d[0],
  89. 'region':region[0],
  90. 'scene' :scene,
  91. 'object': ob
  92. }
  93. '''
  94. print(override)
  95. bpy.ops.object.tissue_update_tessellate(override)
  96. # restore selected objects
  97. if old_mode != None:
  98. for o in scene.objects:
  99. if not o.hide_viewport: o.select_set(o in selected_objects)
  100. bpy.context.view_layer.objects.active = active_object
  101. bpy.ops.object.mode_set(mode=old_mode)
  102. return
  103. def set_tessellate_handler(self, context):
  104. old_handlers = []
  105. blender_handlers = bpy.app.handlers.frame_change_post
  106. for h in blender_handlers:
  107. if "anim_tessellate" in str(h):
  108. old_handlers.append(h)
  109. for h in old_handlers: blender_handlers.remove(h)
  110. for o in context.scene.objects:
  111. if o.tissue_tessellate.bool_run:
  112. blender_handlers.append(anim_tessellate)
  113. break
  114. return
  115. class tissue_tessellate_prop(PropertyGroup):
  116. bool_lock : BoolProperty(
  117. name="Lock",
  118. description="Prevent automatic update on settings changes or if other objects have it in the hierarchy.",
  119. default=False
  120. )
  121. bool_hold : BoolProperty(
  122. name="Hold",
  123. description="Wait...",
  124. default=False
  125. )
  126. bool_dependencies : BoolProperty(
  127. name="Update Dependencies",
  128. description="Automatically updates base and components as well, if results of other tessellations",
  129. default=False
  130. )
  131. bool_run : BoolProperty(
  132. name="Animatable Tessellation",
  133. description="Automatically recompute the tessellation when the frame is changed. Currently is not working during Render Animation",
  134. default = False,
  135. update = set_tessellate_handler
  136. )
  137. zscale : FloatProperty(
  138. name="Scale", default=1, soft_min=0, soft_max=10,
  139. description="Scale factor for the component thickness",
  140. update = anim_tessellate_active
  141. )
  142. scale_mode : EnumProperty(
  143. items=(
  144. ('CONSTANT', "Constant", "Uniform thinkness"),
  145. ('ADAPTIVE', "Relative", "Preserve component's proportions")
  146. ),
  147. default='ADAPTIVE',
  148. name="Z-Scale according to faces size",
  149. update = anim_tessellate_active
  150. )
  151. offset : FloatProperty(
  152. name="Surface Offset",
  153. default=1,
  154. min=-1,
  155. max=1,
  156. soft_min=-1,
  157. soft_max=1,
  158. description="Surface offset",
  159. update = anim_tessellate_active
  160. )
  161. mode : EnumProperty(
  162. items=(
  163. ('BOUNDS', "Bounds", "The component fits automatically the size of the target face"),
  164. ('LOCAL', "Local", "Based on Local coordinates, from 0 to 1"),
  165. ('GLOBAL', 'Global', "Based on Global coordinates, from 0 to 1")),
  166. default='BOUNDS',
  167. name="Component Mode",
  168. update = anim_tessellate_active
  169. )
  170. rotation_mode : EnumProperty(
  171. items=(('RANDOM', "Random", "Random faces rotation"),
  172. ('UV', "Active UV", "Rotate according to UV coordinates"),
  173. ('WEIGHT', "Active Weight", "Rotate according to Vertex Group gradient"),
  174. ('DEFAULT', "Default", "Default rotation")),
  175. default='DEFAULT',
  176. name="Component Rotation",
  177. update = anim_tessellate_active
  178. )
  179. rotation_direction : EnumProperty(
  180. items=(('ORTHO', "Orthogonal", "Component main directions in XY"),
  181. ('DIAG', "Diagonal", "Component main direction aligned with diagonal")),
  182. default='ORTHO',
  183. name="Direction",
  184. update = anim_tessellate_active
  185. )
  186. rotation_shift : IntProperty(
  187. name="Shift",
  188. default=0,
  189. soft_min=0,
  190. soft_max=3,
  191. description="Shift components rotation",
  192. update = anim_tessellate_active
  193. )
  194. fill_mode : EnumProperty(
  195. items=(
  196. ('QUAD', 'Quad', 'Regular quad tessellation. Uses only 3 or 4 vertices'),
  197. ('FAN', 'Fan', 'Radial tessellation for polygonal faces'),
  198. ('PATCH', 'Patch', 'Curved tessellation according to the last ' +
  199. 'Subsurf\n(or Multires) modifiers. Works only with 4 sides ' +
  200. 'patches.\nAfter the last Subsurf (or Multires) only ' +
  201. 'deformation\nmodifiers can be used'),
  202. ('FRAME', 'Frame', 'Essellation along the edges of each face')),
  203. default='QUAD',
  204. name="Fill Mode",
  205. update = anim_tessellate_active
  206. )
  207. combine_mode : EnumProperty(
  208. items=(
  209. ('LAST', 'Last', 'Show only the last iteration'),
  210. ('UNUSED', 'Unused', 'Combine each iteration with the unused faces of the previous iteration. Used for branching systems'),
  211. ('ALL', 'All', 'Combine the result of all iterations')),
  212. default='LAST',
  213. name="Combine Mode",
  214. update = anim_tessellate_active
  215. )
  216. gen_modifiers : BoolProperty(
  217. name="Generator Modifiers",
  218. default=False,
  219. description="Apply Modifiers and Shape Keys to the base object",
  220. update = anim_tessellate_active
  221. )
  222. com_modifiers : BoolProperty(
  223. name="Component Modifiers",
  224. default=False,
  225. description="Apply Modifiers and Shape Keys to the component object",
  226. update = anim_tessellate_active
  227. )
  228. merge : BoolProperty(
  229. name="Merge",
  230. default=False,
  231. description="Merge vertices in adjacent duplicates",
  232. update = anim_tessellate_active
  233. )
  234. merge_thres : FloatProperty(
  235. name="Distance",
  236. default=0.001,
  237. soft_min=0,
  238. soft_max=10,
  239. description="Limit below which to merge vertices",
  240. update = anim_tessellate_active
  241. )
  242. generator : PointerProperty(
  243. type=bpy.types.Object,
  244. name="",
  245. description="Base object for the tessellation",
  246. update = anim_tessellate_active
  247. )
  248. component : PointerProperty(
  249. type=bpy.types.Object,
  250. name="",
  251. description="Component object for the tessellation",
  252. #default="",
  253. update = anim_tessellate_active
  254. )
  255. bool_random : BoolProperty(
  256. name="Randomize",
  257. default=False,
  258. description="Randomize component rotation",
  259. update = anim_tessellate_active
  260. )
  261. random_seed : IntProperty(
  262. name="Seed",
  263. default=0,
  264. soft_min=0,
  265. soft_max=10,
  266. description="Random seed",
  267. update = anim_tessellate_active
  268. )
  269. bool_vertex_group : BoolProperty(
  270. name="Map Vertex Group",
  271. default=False,
  272. description="Transfer all Vertex Groups from Base object",
  273. update = anim_tessellate_active
  274. )
  275. bool_selection : BoolProperty(
  276. name="On selected Faces",
  277. default=False,
  278. description="Create Tessellation only on selected faces",
  279. update = anim_tessellate_active
  280. )
  281. bool_shapekeys : BoolProperty(
  282. name="Use Shape Keys",
  283. default=False,
  284. description="Transfer Component's Shape Keys. If the name of Vertex "
  285. "Groups and Shape Keys are the same, they will be "
  286. "automatically combined",
  287. update = anim_tessellate_active
  288. )
  289. bool_smooth : BoolProperty(
  290. name="Smooth Shading",
  291. default=False,
  292. description="Output faces with smooth shading rather than flat shaded",
  293. update = anim_tessellate_active
  294. )
  295. bool_materials : BoolProperty(
  296. name="Transfer Materials",
  297. default=False,
  298. description="Preserve component's materials",
  299. update = anim_tessellate_active
  300. )
  301. bool_material_id : BoolProperty(
  302. name="Tessellation on Material ID",
  303. default=False,
  304. description="Apply the component only on the selected Material",
  305. update = anim_tessellate_active
  306. )
  307. material_id : IntProperty(
  308. name="Material ID",
  309. default=0,
  310. min=0,
  311. description="Material ID",
  312. update = anim_tessellate_active
  313. )
  314. bool_dissolve_seams : BoolProperty(
  315. name="Dissolve Seams",
  316. default=False,
  317. description="Dissolve all seam edges",
  318. update = anim_tessellate_active
  319. )
  320. iterations : IntProperty(
  321. name="Iterations",
  322. default=1,
  323. min=1,
  324. soft_max=5,
  325. description="Automatically repeat the Tessellation using the "
  326. + "generated geometry as new base object.\nUsefull for "
  327. + "for branching systems. Dangerous!",
  328. update = anim_tessellate_active
  329. )
  330. bool_combine : BoolProperty(
  331. name="Combine unused",
  332. default=False,
  333. description="Combine the generated geometry with unused faces",
  334. update = anim_tessellate_active
  335. )
  336. bool_advanced : BoolProperty(
  337. name="Advanced Settings",
  338. default=False,
  339. description="Show more settings"
  340. )
  341. normals_mode : EnumProperty(
  342. items=(
  343. ('VERTS', 'Normals', 'Consistent direction based on vertices normal'),
  344. ('FACES', 'Individual Faces', 'Based on individual faces normal'),
  345. ('CUSTOM', 'Custom', "According to Base object's shape keys")),
  346. default='VERTS',
  347. name="Direction",
  348. update = anim_tessellate_active
  349. )
  350. bool_multi_components : BoolProperty(
  351. name="Multi Components",
  352. default=False,
  353. description="Combine different components according to materials name",
  354. update = anim_tessellate_active
  355. )
  356. error_message : StringProperty(
  357. name="Error Message",
  358. default=""
  359. )
  360. warning_message : StringProperty(
  361. name="Warning Message",
  362. default=""
  363. )
  364. bounds_x : EnumProperty(
  365. items=(
  366. ('EXTEND', 'Extend', 'Default X coordinates'),
  367. ('CLIP', 'Clip', 'Trim out of bounds in X direction'),
  368. ('CYCLIC', 'Cyclic', 'Cyclic components in X direction')),
  369. default='EXTEND',
  370. name="Bounds X",
  371. update = anim_tessellate_active
  372. )
  373. bounds_y : EnumProperty(
  374. items=(
  375. ('EXTEND', 'Extend', 'Default Y coordinates'),
  376. ('CLIP', 'Clip', 'Trim out of bounds in Y direction'),
  377. ('CYCLIC', 'Cyclic', 'Cyclic components in Y direction')),
  378. default='EXTEND',
  379. name="Bounds Y",
  380. update = anim_tessellate_active
  381. )
  382. close_mesh : EnumProperty(
  383. items=(
  384. ('NONE', 'None', 'Keep the mesh open'),
  385. ('CAP', 'Cap Holes', 'Automatically cap open loops'),
  386. ('BRIDGE', 'Bridge Loops', 'Automatically bridge loop pairs')),
  387. default='NONE',
  388. name="Close Mesh",
  389. update = anim_tessellate_active
  390. )
  391. cap_faces : BoolProperty(
  392. name="Cap Holes",
  393. default=False,
  394. description="Cap open edges loops",
  395. update = anim_tessellate_active
  396. )
  397. frame_boundary : BoolProperty(
  398. name="Frame Boundary",
  399. default=False,
  400. description="Support face boundaries",
  401. update = anim_tessellate_active
  402. )
  403. fill_frame : BoolProperty(
  404. name="Fill Frame",
  405. default=False,
  406. description="Fill inner faces with Fan tessellation",
  407. update = anim_tessellate_active
  408. )
  409. frame_boundary_mat : IntProperty(
  410. name="Material Offset",
  411. default=0,
  412. description="Material Offset for boundaries",
  413. update = anim_tessellate_active
  414. )
  415. fill_frame_mat : IntProperty(
  416. name="Material Offset",
  417. default=0,
  418. description="Material Offset for inner faces",
  419. update = anim_tessellate_active
  420. )
  421. open_edges_crease : FloatProperty(
  422. name="Open Edges Crease",
  423. default=0,
  424. min=0,
  425. max=1,
  426. description="Automatically set crease for open edges",
  427. update = anim_tessellate_active
  428. )
  429. bridge_smoothness : FloatProperty(
  430. name="Smoothness",
  431. default=1,
  432. min=0,
  433. max=1,
  434. description="Bridge Smoothness",
  435. update = anim_tessellate_active
  436. )
  437. frame_thickness : FloatProperty(
  438. name="Frame Thickness",
  439. default=0.2,
  440. min=0,
  441. soft_max=2,
  442. description="Frame Thickness",
  443. update = anim_tessellate_active
  444. )
  445. frame_mode : EnumProperty(
  446. items=(
  447. ('CONSTANT', 'Constant', 'Even thickness'),
  448. ('RELATIVE', 'Relative', 'Frame offset depends on face areas')),
  449. default='CONSTANT',
  450. name="Offset",
  451. update = anim_tessellate_active
  452. )
  453. bridge_cuts : IntProperty(
  454. name="Cuts",
  455. default=0,
  456. min=0,
  457. max=20,
  458. description="Bridge Cuts",
  459. update = anim_tessellate_active
  460. )
  461. cap_material_index : IntProperty(
  462. name="Material",
  463. default=0,
  464. min=0,
  465. description="Material index for the cap/bridge faces",
  466. update = anim_tessellate_active
  467. )
  468. patch_subs : IntProperty(
  469. name="Patch Subdivisions",
  470. default=1,
  471. min=0,
  472. description="Subdivisions levels for Patch tessellation after the first iteration",
  473. update = anim_tessellate_active
  474. )
  475. def store_parameters(operator, ob):
  476. ob.tissue_tessellate.bool_hold = True
  477. ob.tissue_tessellate.bool_lock = operator.bool_lock
  478. ob.tissue_tessellate.bool_dependencies = operator.bool_dependencies
  479. ob.tissue_tessellate.generator = bpy.data.objects[operator.generator]
  480. ob.tissue_tessellate.component = bpy.data.objects[operator.component]
  481. ob.tissue_tessellate.zscale = operator.zscale
  482. ob.tissue_tessellate.offset = operator.offset
  483. ob.tissue_tessellate.gen_modifiers = operator.gen_modifiers
  484. ob.tissue_tessellate.com_modifiers = operator.com_modifiers
  485. ob.tissue_tessellate.mode = operator.mode
  486. ob.tissue_tessellate.rotation_mode = operator.rotation_mode
  487. ob.tissue_tessellate.rotation_shift = operator.rotation_shift
  488. ob.tissue_tessellate.rotation_direction = operator.rotation_direction
  489. ob.tissue_tessellate.merge = operator.merge
  490. ob.tissue_tessellate.merge_thres = operator.merge_thres
  491. ob.tissue_tessellate.scale_mode = operator.scale_mode
  492. ob.tissue_tessellate.bool_random = operator.bool_random
  493. ob.tissue_tessellate.random_seed = operator.random_seed
  494. ob.tissue_tessellate.fill_mode = operator.fill_mode
  495. ob.tissue_tessellate.bool_vertex_group = operator.bool_vertex_group
  496. ob.tissue_tessellate.bool_selection = operator.bool_selection
  497. ob.tissue_tessellate.bool_shapekeys = operator.bool_shapekeys
  498. ob.tissue_tessellate.bool_smooth = operator.bool_smooth
  499. ob.tissue_tessellate.bool_materials = operator.bool_materials
  500. ob.tissue_tessellate.bool_material_id = operator.bool_material_id
  501. ob.tissue_tessellate.material_id = operator.material_id
  502. ob.tissue_tessellate.bool_dissolve_seams = operator.bool_dissolve_seams
  503. ob.tissue_tessellate.iterations = operator.iterations
  504. ob.tissue_tessellate.bool_advanced = operator.bool_advanced
  505. ob.tissue_tessellate.normals_mode = operator.normals_mode
  506. ob.tissue_tessellate.bool_combine = operator.bool_combine
  507. ob.tissue_tessellate.bool_multi_components = operator.bool_multi_components
  508. ob.tissue_tessellate.combine_mode = operator.combine_mode
  509. ob.tissue_tessellate.bounds_x = operator.bounds_x
  510. ob.tissue_tessellate.bounds_y = operator.bounds_y
  511. ob.tissue_tessellate.cap_faces = operator.cap_faces
  512. ob.tissue_tessellate.close_mesh = operator.close_mesh
  513. ob.tissue_tessellate.bridge_cuts = operator.bridge_cuts
  514. ob.tissue_tessellate.bridge_smoothness = operator.bridge_smoothness
  515. ob.tissue_tessellate.frame_thickness = operator.frame_thickness
  516. ob.tissue_tessellate.frame_mode = operator.frame_mode
  517. ob.tissue_tessellate.frame_boundary = operator.frame_boundary
  518. ob.tissue_tessellate.fill_frame = operator.fill_frame
  519. ob.tissue_tessellate.frame_boundary_mat = operator.frame_boundary_mat
  520. ob.tissue_tessellate.fill_frame_mat = operator.fill_frame_mat
  521. ob.tissue_tessellate.cap_material_index = operator.cap_material_index
  522. ob.tissue_tessellate.patch_subs = operator.patch_subs
  523. ob.tissue_tessellate.bool_hold = False
  524. return ob
  525. def load_parameters(operator, ob):
  526. operator.bool_lock = ob.tissue_tessellate.bool_lock
  527. operator.bool_dependencies = ob.tissue_tessellate.bool_dependencies
  528. operator.generator = ob.tissue_tessellate.generator.name
  529. operator.component = ob.tissue_tessellate.component.name
  530. operator.zscale = ob.tissue_tessellate.zscale
  531. operator.offset = ob.tissue_tessellate.offset
  532. operator.gen_modifiers = ob.tissue_tessellate.gen_modifiers
  533. operator.com_modifiers = ob.tissue_tessellate.com_modifiers
  534. operator.mode = ob.tissue_tessellate.mode
  535. operator.rotation_mode = ob.tissue_tessellate.rotation_mode
  536. operator.rotation_shift = ob.tissue_tessellate.rotation_shift
  537. operator.rotation_direction = ob.tissue_tessellate.rotation_direction
  538. operator.merge = ob.tissue_tessellate.merge
  539. operator.merge_thres = ob.tissue_tessellate.merge_thres
  540. operator.scale_mode = ob.tissue_tessellate.scale_mode
  541. operator.bool_random = ob.tissue_tessellate.bool_random
  542. operator.random_seed = ob.tissue_tessellate.random_seed
  543. operator.fill_mode = ob.tissue_tessellate.fill_mode
  544. operator.bool_vertex_group = ob.tissue_tessellate.bool_vertex_group
  545. operator.bool_selection = ob.tissue_tessellate.bool_selection
  546. operator.bool_shapekeys = ob.tissue_tessellate.bool_shapekeys
  547. operator.bool_smooth = ob.tissue_tessellate.bool_smooth
  548. operator.bool_materials = ob.tissue_tessellate.bool_materials
  549. operator.bool_material_id = ob.tissue_tessellate.bool_material_id
  550. operator.material_id = ob.tissue_tessellate.material_id
  551. operator.bool_dissolve_seams = ob.tissue_tessellate.bool_dissolve_seams
  552. operator.iterations = ob.tissue_tessellate.iterations
  553. operator.bool_advanced = ob.tissue_tessellate.bool_advanced
  554. operator.normals_mode = ob.tissue_tessellate.normals_mode
  555. operator.bool_combine = ob.tissue_tessellate.bool_combine
  556. operator.bool_multi_components = ob.tissue_tessellate.bool_multi_components
  557. operator.combine_mode = ob.tissue_tessellate.combine_mode
  558. operator.bounds_x = ob.tissue_tessellate.bounds_x
  559. operator.bounds_y = ob.tissue_tessellate.bounds_y
  560. operator.cap_faces = ob.tissue_tessellate.cap_faces
  561. operator.close_mesh = ob.tissue_tessellate.close_mesh
  562. operator.bridge_cuts = ob.tissue_tessellate.bridge_cuts
  563. operator.bridge_smoothness = ob.tissue_tessellate.bridge_smoothness
  564. operator.cap_material_index = ob.tissue_tessellate.cap_material_index
  565. operator.patch_subs = ob.tissue_tessellate.patch_subs
  566. operator.frame_boundary = ob.tissue_tessellate.frame_boundary
  567. operator.fill_frame = ob.tissue_tessellate.fill_frame
  568. operator.frame_boundary_mat = ob.tissue_tessellate.frame_boundary_mat
  569. operator.fill_frame_mat = ob.tissue_tessellate.fill_frame_mat
  570. operator.frame_thickness = ob.tissue_tessellate.frame_thickness
  571. operator.frame_mode = ob.tissue_tessellate.frame_mode
  572. return ob
  573. def tessellate_patch(_ob0, _ob1, offset, zscale, com_modifiers, mode,
  574. scale_mode, rotation_mode, rotation_shift, rand_seed, bool_vertex_group,
  575. bool_selection, bool_shapekeys, bool_material_id, material_id,
  576. normals_mode, bounds_x, bounds_y):
  577. random.seed(rand_seed)
  578. if normals_mode == 'CUSTOM':
  579. if _ob0.data.shape_keys != None:
  580. ob0_sk = convert_object_to_mesh(_ob0)
  581. me0_sk = ob0_sk.data
  582. key_values0 = [sk.value for sk in _ob0.data.shape_keys.key_blocks]
  583. for sk in _ob0.data.shape_keys.key_blocks: sk.value = 0
  584. else: normals_mode = 'VERTS'
  585. ob0 = convert_object_to_mesh(_ob0)
  586. me0 = ob0.data
  587. # base normals
  588. normals0 = []
  589. if normals_mode == 'CUSTOM':
  590. for sk, val in zip(_ob0.data.shape_keys.key_blocks, key_values0): sk.value = val
  591. for v0, v1 in zip(ob0.data.vertices, me0_sk.vertices):
  592. normals0.append(v1.co - v0.co)
  593. bpy.data.objects.remove(ob0_sk)
  594. else:
  595. ob0.data.update()
  596. normals0 = [v.normal for v in ob0.data.vertices]
  597. # ob0 = convert_object_to_mesh(_ob0)
  598. ob0.name = _ob0.name + "_apply_mod"
  599. me0 = _ob0.data
  600. # Check if zero faces are selected
  601. if _ob0.type == 'MESH':
  602. bool_cancel = True
  603. for p in me0.polygons:
  604. check_sel = check_mat = False
  605. if not bool_selection or p.select: check_sel = True
  606. if not bool_material_id or p.material_index == material_id: check_mat = True
  607. if check_sel and check_mat:
  608. bool_cancel = False
  609. break
  610. if bool_cancel:
  611. bpy.data.meshes.remove(ob0.data)
  612. #bpy.data.objects.remove(ob0)
  613. return 0
  614. levels = 0
  615. sculpt_levels = 0
  616. render_levels = 0
  617. bool_multires = False
  618. multires_name = ""
  619. not_allowed = ['FLUID_SIMULATION', 'ARRAY', 'BEVEL', 'BOOLEAN', 'BUILD',
  620. 'DECIMATE', 'EDGE_SPLIT', 'MASK', 'MIRROR', 'REMESH',
  621. 'SCREW', 'SOLIDIFY', 'TRIANGULATE', 'WIREFRAME', 'SKIN',
  622. 'EXPLODE', 'PARTICLE_INSTANCE', 'PARTICLE_SYSTEM', 'SMOKE']
  623. modifiers0 = list(_ob0.modifiers)#[m for m in ob0.modifiers]
  624. show_modifiers = [m.show_viewport for m in _ob0.modifiers]
  625. show_modifiers.reverse()
  626. modifiers0.reverse()
  627. for m in modifiers0:
  628. visible = m.show_viewport
  629. if not visible: continue
  630. #m.show_viewport = False
  631. if m.type in ('SUBSURF', 'MULTIRES') and visible:
  632. levels = m.levels
  633. multires_name = m.name
  634. if m.type == 'MULTIRES':
  635. bool_multires = True
  636. multires_name = m.name
  637. sculpt_levels = m.sculpt_levels
  638. render_levels = m.render_levels
  639. else: bool_multires = False
  640. break
  641. elif m.type in not_allowed:
  642. bpy.data.meshes.remove(ob0.data)
  643. #bpy.data.meshes.remove(me0)
  644. return "modifiers_error"
  645. before = _ob0.copy()
  646. before.name = _ob0.name + "_before_subs"
  647. bpy.context.collection.objects.link(before)
  648. #if ob0.type == 'MESH': before.data = me0
  649. before_mod = list(before.modifiers)
  650. before_mod.reverse()
  651. for m in before_mod:
  652. if m.type in ('SUBSURF', 'MULTIRES') and m.show_viewport:
  653. before.modifiers.remove(m)
  654. break
  655. else: before.modifiers.remove(m)
  656. before_subsurf = simple_to_mesh(before)
  657. before_bm = bmesh.new()
  658. before_bm.from_mesh(before_subsurf)
  659. before_bm.faces.ensure_lookup_table()
  660. before_bm.edges.ensure_lookup_table()
  661. before_bm.verts.ensure_lookup_table()
  662. error = ""
  663. for f in before_bm.faces:
  664. if len(f.loops) != 4:
  665. error = "topology_error"
  666. break
  667. for e in before_bm.edges:
  668. if len(e.link_faces) == 0:
  669. error = "wires_error"
  670. break
  671. for v in before_bm.verts:
  672. if len(v.link_faces) == 0:
  673. error = "verts_error"
  674. break
  675. if error != "":
  676. bpy.data.meshes.remove(ob0.data)
  677. #bpy.data.meshes.remove(me0)
  678. bpy.data.meshes.remove(before_subsurf)
  679. bpy.data.objects.remove(before)
  680. return error
  681. me0 = ob0.data
  682. verts0 = me0.vertices # Collect generator vertices
  683. if com_modifiers or _ob1.type != 'MESH': bool_shapekeys = False
  684. # set Shape Keys to zero
  685. if bool_shapekeys or not com_modifiers:
  686. try:
  687. original_key_values = []
  688. for sk in _ob1.data.shape_keys.key_blocks:
  689. original_key_values.append(sk.value)
  690. sk.value = 0
  691. except:
  692. bool_shapekeys = False
  693. if not com_modifiers and not bool_shapekeys:
  694. mod_visibility = []
  695. for m in _ob1.modifiers:
  696. mod_visibility.append(m.show_viewport)
  697. m.show_viewport = False
  698. com_modifiers = True
  699. ob1 = convert_object_to_mesh(_ob1, com_modifiers, False)
  700. me1 = ob1.data
  701. if mode != 'BOUNDS':
  702. ob1.active_shape_key_index = 0
  703. # Bound X
  704. if bounds_x != 'EXTEND':
  705. if mode == 'GLOBAL':
  706. planes_co = ((0,0,0),(1,1,1))
  707. plane_no = (1,0,0)
  708. if mode == 'LOCAL':
  709. planes_co = (ob1.matrix_world @ Vector((0,0,0)), ob1.matrix_world @ Vector((1,0,0)))
  710. plane_no = planes_co[0]-planes_co[1]
  711. bpy.ops.object.mode_set(mode='EDIT')
  712. for co in planes_co:
  713. bpy.ops.mesh.select_all(action='SELECT')
  714. bpy.ops.mesh.bisect(plane_co=co, plane_no=plane_no)
  715. bpy.ops.mesh.mark_seam()
  716. bpy.ops.object.mode_set(mode='OBJECT')
  717. _faces = ob1.data.polygons
  718. if mode == 'GLOBAL':
  719. for f in [f for f in _faces if (ob1.matrix_world @ f.center).x > 1]:
  720. f.select = True
  721. for f in [f for f in _faces if (ob1.matrix_world @ f.center).x < 0]:
  722. f.select = True
  723. else:
  724. for f in [f for f in _faces if f.center.x > 1]:
  725. f.select = True
  726. for f in [f for f in _faces if f.center.x < 0]:
  727. f.select = True
  728. bpy.ops.object.mode_set(mode='EDIT')
  729. bpy.ops.mesh.select_mode(type='FACE')
  730. if bounds_x == 'CLIP':
  731. bpy.ops.mesh.delete(type='FACE')
  732. bpy.ops.object.mode_set(mode='OBJECT')
  733. if bounds_x == 'CYCLIC':
  734. bpy.ops.mesh.split()
  735. bpy.ops.object.mode_set(mode='OBJECT')
  736. # Bound Y
  737. if bounds_y != 'EXTEND':
  738. if mode == 'GLOBAL':
  739. planes_co = ((0,0,0),(1,1,1))
  740. plane_no = (0,1,0)
  741. if mode == 'LOCAL':
  742. planes_co = (ob1.matrix_world @ Vector((0,0,0)), ob1.matrix_world @ Vector((0,1,0)))
  743. plane_no = planes_co[0]-planes_co[1]
  744. bpy.ops.object.mode_set(mode='EDIT')
  745. for co in planes_co:
  746. bpy.ops.mesh.select_all(action='SELECT')
  747. bpy.ops.mesh.bisect(plane_co=co, plane_no=plane_no)
  748. bpy.ops.mesh.mark_seam()
  749. bpy.ops.object.mode_set(mode='OBJECT')
  750. _faces = ob1.data.polygons
  751. if mode == 'GLOBAL':
  752. for f in [f for f in _faces if (ob1.matrix_world @ f.center).y > 1]:
  753. f.select = True
  754. for f in [f for f in _faces if (ob1.matrix_world @ f.center).y < 0]:
  755. f.select = True
  756. else:
  757. for f in [f for f in _faces if f.center.y > 1]:
  758. f.select = True
  759. for f in [f for f in _faces if f.center.y < 0]:
  760. f.select = True
  761. bpy.ops.object.mode_set(mode='EDIT')
  762. bpy.ops.mesh.select_mode(type='FACE')
  763. if bounds_y == 'CLIP':
  764. bpy.ops.mesh.delete(type='FACE')
  765. bpy.ops.object.mode_set(mode='OBJECT')
  766. if bounds_y == 'CYCLIC':
  767. bpy.ops.mesh.split()
  768. bpy.ops.object.mode_set(mode='OBJECT')
  769. bpy.ops.object.mode_set(mode='OBJECT')
  770. # Component statistics
  771. n_verts = len(me1.vertices)
  772. # Create empty lists
  773. new_verts = []
  774. new_edges = []
  775. new_faces = []
  776. new_verts_np = np.array(())
  777. # Component bounding box
  778. min_c = Vector((0, 0, 0))
  779. max_c = Vector((0, 0, 0))
  780. first = True
  781. for v in me1.vertices:
  782. vert = v.co
  783. if vert[0] < min_c[0] or first:
  784. min_c[0] = vert[0]
  785. if vert[1] < min_c[1] or first:
  786. min_c[1] = vert[1]
  787. if vert[2] < min_c[2] or first:
  788. min_c[2] = vert[2]
  789. if vert[0] > max_c[0] or first:
  790. max_c[0] = vert[0]
  791. if vert[1] > max_c[1] or first:
  792. max_c[1] = vert[1]
  793. if vert[2] > max_c[2] or first:
  794. max_c[2] = vert[2]
  795. first = False
  796. bb = max_c - min_c
  797. # adaptive XY
  798. verts1 = []
  799. for v in me1.vertices:
  800. if mode == 'BOUNDS':
  801. vert = v.co - min_c # (ob1.matrix_world * v.co) - min_c
  802. vert[0] = vert[0] / bb[0] if bb[0] != 0 else 0.5
  803. vert[1] = vert[1] / bb[1] if bb[1] != 0 else 0.5
  804. vert[2] = vert[2] / bb[2] if bb[2] != 0 else 0
  805. vert[2] = (vert[2] - 0.5 + offset * 0.5) * zscale
  806. elif mode == 'LOCAL':
  807. vert = v.co.xyz
  808. vert[2] *= zscale
  809. #vert[2] = (vert[2] - min_c[2] + (-0.5 + offset * 0.5) * bb[2]) * zscale
  810. elif mode == 'GLOBAL':
  811. vert = ob1.matrix_world @ v.co
  812. vert[2] *= zscale
  813. try:
  814. for sk in me1.shape_keys.key_blocks:
  815. sk.data[v.index].co = ob1.matrix_world @ sk.data[v.index].co
  816. except: pass
  817. #verts1.append(vert)
  818. v.co = vert
  819. # Bounds X, Y
  820. if mode != 'BOUNDS':
  821. if bounds_x == 'CYCLIC':
  822. move_verts = []
  823. for f in [f for f in me1.polygons if (f.center).x > 1]:
  824. for v in f.vertices:
  825. if v not in move_verts: move_verts.append(v)
  826. for v in move_verts:
  827. me1.vertices[v].co.x -= 1
  828. try:
  829. _ob1.active_shape_key_index = 0
  830. for sk in me1.shape_keys.key_blocks:
  831. sk.data[v].co.x -= 1
  832. except: pass
  833. move_verts = []
  834. for f in [f for f in me1.polygons if (f.center).x < 0]:
  835. for v in f.vertices:
  836. if v not in move_verts: move_verts.append(v)
  837. for v in move_verts:
  838. me1.vertices[v].co.x += 1
  839. try:
  840. _ob1.active_shape_key_index = 0
  841. for sk in me1.shape_keys.key_blocks:
  842. sk.data[v].co.x += 1
  843. except: pass
  844. if bounds_y == 'CYCLIC':
  845. move_verts = []
  846. for f in [f for f in me1.polygons if (f.center).y > 1]:
  847. for v in f.vertices:
  848. if v not in move_verts: move_verts.append(v)
  849. for v in move_verts:
  850. me1.vertices[v].co.y -= 1
  851. try:
  852. _ob1.active_shape_key_index = 0
  853. for sk in me1.shape_keys.key_blocks:
  854. sk.data[v].co.y -= 1
  855. except: pass
  856. move_verts = []
  857. for f in [f for f in me1.polygons if (f.center).y < 0]:
  858. for v in f.vertices:
  859. if v not in move_verts: move_verts.append(v)
  860. for v in move_verts:
  861. me1.vertices[v].co.y += 1
  862. try:
  863. _ob1.active_shape_key_index = 0
  864. for sk in me1.shape_keys.key_blocks:
  865. sk.data[v].co.y += 1
  866. except: pass
  867. verts1 = [v.co for v in me1.vertices]
  868. n_verts1 = len(verts1)
  869. patch_faces = 4**levels
  870. sides = int(sqrt(patch_faces))
  871. step = 1/sides
  872. sides0 = sides-2
  873. patch_faces0 = int((sides-2)**2)
  874. n_patches = int(len(me0.polygons)/patch_faces)
  875. if len(me0.polygons)%patch_faces != 0:
  876. #ob0.data = old_me0
  877. return "topology_error"
  878. new_verts = []
  879. new_edges = []
  880. new_faces = []
  881. for o in bpy.context.view_layer.objects: o.select_set(False)
  882. new_patch = None
  883. # All vertex group
  884. if bool_vertex_group:
  885. try:
  886. weight = []
  887. for vg in ob0.vertex_groups:
  888. _weight = []
  889. for v in me0.vertices:
  890. try:
  891. _weight.append(vg.weight(v.index))
  892. except:
  893. _weight.append(0)
  894. weight.append(_weight)
  895. except:
  896. bool_vertex_group = False
  897. # Adaptive Z
  898. if scale_mode == 'ADAPTIVE':
  899. com_area = bb[0]*bb[1]
  900. if mode != 'BOUNDS' or com_area == 0: com_area = 1
  901. #mult = 1/com_area
  902. verts_area = []
  903. bm = bmesh.new()
  904. bm.from_mesh(me0)
  905. bm.verts.ensure_lookup_table()
  906. for v in bm.verts:
  907. area = 0
  908. faces = v.link_faces
  909. for f in faces:
  910. area += f.calc_area()
  911. area = area/len(faces)*patch_faces/com_area
  912. #area*=mult*
  913. verts_area.append(sqrt(area)*bb[2])
  914. random.seed(rand_seed)
  915. bool_correct = False
  916. _faces = [[[0] for ii in range(sides)] for jj in range(sides)]
  917. _verts = [[[0] for ii in range(sides+1)] for jj in range(sides+1)]
  918. # find relative UV component's vertices
  919. verts1_uv_quads = [0]*len(verts1)
  920. verts1_uv = [0]*len(verts1)
  921. for i, vert in enumerate(verts1):
  922. # grid coordinates
  923. u = int(vert[0]//step)
  924. v = int(vert[1]//step)
  925. u1 = min(u+1, sides)
  926. v1 = min(v+1, sides)
  927. if mode != 'BOUNDS':
  928. if u > sides-1:
  929. u = sides-1
  930. u1 = sides
  931. if u < 0:
  932. u = 0
  933. u1 = 1
  934. if v > sides-1:
  935. v = sides-1
  936. v1 = sides
  937. if v < 0:
  938. v = 0
  939. v1 = 1
  940. verts1_uv_quads[i] = (u,v,u1,v1)
  941. # factor coordinates
  942. fu = (vert[0]-u*step)/step
  943. fv = (vert[1]-v*step)/step
  944. fw = vert.z
  945. # interpolate Z scaling factor
  946. verts1_uv[i] = Vector((fu,fv,fw))
  947. sk_uv_quads = []
  948. sk_uv = []
  949. if bool_shapekeys:
  950. for sk in ob1.data.shape_keys.key_blocks:
  951. source = sk.data
  952. _sk_uv_quads = [0]*len(verts1)
  953. _sk_uv = [0]*len(verts1)
  954. for i, sk_v in enumerate(source):
  955. if mode == 'BOUNDS':
  956. sk_vert = sk_v.co - min_c
  957. sk_vert[0] = (sk_vert[0] / bb[0] if bb[0] != 0 else 0.5)
  958. sk_vert[1] = (sk_vert[1] / bb[1] if bb[1] != 0 else 0.5)
  959. sk_vert[2] = (sk_vert[2] / bb[2] if bb[2] != 0 else sk_vert[2])
  960. sk_vert[2] = (sk_vert[2] - 0.5 + offset * 0.5) * zscale
  961. elif mode == 'LOCAL':
  962. sk_vert = sk_v.co
  963. sk_vert[2] *= zscale
  964. elif mode == 'GLOBAL':
  965. sk_vert = sk_v.co
  966. sk_vert[2] *= zscale
  967. # grid coordinates
  968. u = int(sk_vert[0]//step)
  969. v = int(sk_vert[1]//step)
  970. u1 = min(u+1, sides)
  971. v1 = min(v+1, sides)
  972. if mode != 'BOUNDS':
  973. if u > sides-1:
  974. u = sides-1
  975. u1 = sides
  976. if u < 0:
  977. u = 0
  978. u1 = 1
  979. if v > sides-1:
  980. v = sides-1
  981. v1 = sides
  982. if v < 0:
  983. v = 0
  984. v1 = 1
  985. _sk_uv_quads[i] = (u,v,u1,v1)
  986. # factor coordinates
  987. fu = (sk_vert[0]-u*step)/step
  988. fv = (sk_vert[1]-v*step)/step
  989. fw = sk_vert.z
  990. _sk_uv[i] = Vector((fu,fv,fw))
  991. sk_uv_quads.append(_sk_uv_quads)
  992. sk_uv.append(_sk_uv)
  993. for i in range(n_patches):
  994. poly = me0.polygons[i*patch_faces]
  995. if bool_selection and not poly.select: continue
  996. if bool_material_id and not poly.material_index == material_id: continue
  997. bool_correct = True
  998. new_patch = bpy.data.objects.new("patch", me1.copy())
  999. bpy.context.collection.objects.link(new_patch)
  1000. new_patch.select_set(True)
  1001. bpy.context.view_layer.objects.active = new_patch
  1002. for area in bpy.context.screen.areas:
  1003. for space in area.spaces:
  1004. try: new_patch.local_view_set(space, True)
  1005. except: pass
  1006. # Vertex Group
  1007. if bool_vertex_group:
  1008. for vg in ob0.vertex_groups:
  1009. new_patch.vertex_groups.new(name=vg.name)
  1010. # find patch faces
  1011. faces = _faces.copy()
  1012. verts = _verts.copy()
  1013. shift1 = sides
  1014. shift2 = sides*2-1
  1015. shift3 = sides*3-2
  1016. for j in range(patch_faces):
  1017. if j < patch_faces0:
  1018. if levels == 0:
  1019. u = j%sides0
  1020. v = j//sides0
  1021. else:
  1022. u = j%sides0+1
  1023. v = j//sides0+1
  1024. elif j < patch_faces0 + shift1:
  1025. u = j-patch_faces0
  1026. v = 0
  1027. elif j < patch_faces0 + shift2:
  1028. u = sides-1
  1029. v = j-(patch_faces0 + sides)+1
  1030. elif j < patch_faces0 + shift3:
  1031. jj = j-(patch_faces0 + shift2)
  1032. u = sides-jj-2
  1033. v = sides-1
  1034. else:
  1035. jj = j-(patch_faces0 + shift3)
  1036. u = 0
  1037. v = sides-jj-2
  1038. face = me0.polygons[j+i*patch_faces]
  1039. faces[u][v] = face
  1040. verts[u][v] = verts0[face.vertices[0]]
  1041. if u == sides-1:
  1042. verts[sides][v] = verts0[face.vertices[1]]
  1043. if v == sides-1:
  1044. verts[u][sides] = verts0[face.vertices[3]]
  1045. if u == v == sides-1:
  1046. verts[sides][sides] = verts0[face.vertices[2]]
  1047. # Random rotation
  1048. if rotation_mode == 'RANDOM' or rotation_shift != 0:
  1049. if rotation_mode == 'RANDOM': rot = random.randint(0, 3)
  1050. else: rot = rotation_shift%4
  1051. if rot == 1:
  1052. verts = [[verts[w][k] for w in range(sides+1)] for k in range(sides,-1,-1)]
  1053. elif rot == 2:
  1054. verts = [[verts[k][w] for w in range(sides,-1,-1)] for k in range(sides,-1,-1)]
  1055. elif rot == 3:
  1056. verts = [[verts[w][k] for w in range(sides,-1,-1)] for k in range(sides+1)]
  1057. # UV rotation
  1058. if rotation_mode == 'UV' and ob0.type == 'MESH':
  1059. if len(ob0.data.uv_layers) > 0:
  1060. uv0 = me0.uv_layers.active.data[faces[0][0].index*4].uv
  1061. uv1 = me0.uv_layers.active.data[faces[0][-1].index*4 + 3].uv
  1062. uv2 = me0.uv_layers.active.data[faces[-1][-1].index*4 + 2].uv
  1063. uv3 = me0.uv_layers.active.data[faces[-1][0].index*4 + 1].uv
  1064. v01 = (uv0 + uv1)
  1065. v32 = (uv3 + uv2)
  1066. v0132 = v32 - v01
  1067. v0132.normalize()
  1068. v12 = (uv1 + uv2)
  1069. v03 = (uv0 + uv3)
  1070. v1203 = v03 - v12
  1071. v1203.normalize()
  1072. vertUV = []
  1073. dot1203 = v1203.x
  1074. dot0132 = v0132.x
  1075. if(abs(dot1203) < abs(dot0132)):
  1076. if (dot0132 > 0):
  1077. pass
  1078. else:
  1079. verts = [[verts[k][w] for w in range(sides,-1,-1)] for k in range(sides,-1,-1)]
  1080. else:
  1081. if(dot1203 < 0):
  1082. verts = [[verts[w][k] for w in range(sides,-1,-1)] for k in range(sides+1)]
  1083. else:
  1084. verts = [[verts[w][k] for w in range(sides+1)] for k in range(sides,-1,-1)]
  1085. if True:
  1086. verts_xyz = np.array([[v.co for v in _verts] for _verts in verts])
  1087. #verts_norm = np.array([[v.normal for v in _verts] for _verts in verts])
  1088. verts_norm = np.array([[normals0[v.index] for v in _verts] for _verts in verts])
  1089. if normals_mode == 'FACES':
  1090. verts_norm = np.mean(verts_norm, axis=(0,1))
  1091. verts_norm = np.expand_dims(verts_norm, axis=0)
  1092. verts_norm = np.repeat(verts_norm,len(verts),axis=0)
  1093. verts_norm = np.expand_dims(verts_norm, axis=0)
  1094. verts_norm = np.repeat(verts_norm,len(verts),axis=0)
  1095. np_verts1_uv = np.array(verts1_uv)
  1096. verts1_uv_quads = np.array(verts1_uv_quads)
  1097. u = verts1_uv_quads[:,0]
  1098. v = verts1_uv_quads[:,1]
  1099. u1 = verts1_uv_quads[:,2]
  1100. v1 = verts1_uv_quads[:,3]
  1101. v00 = verts_xyz[u,v]
  1102. v10 = verts_xyz[u1,v]
  1103. v01 = verts_xyz[u,v1]
  1104. v11 = verts_xyz[u1,v1]
  1105. n00 = verts_norm[u,v]
  1106. n10 = verts_norm[u1,v]
  1107. n01 = verts_norm[u,v1]
  1108. n11 = verts_norm[u1,v1]
  1109. vx = np_verts1_uv[:,0].reshape((n_verts1,1))
  1110. vy = np_verts1_uv[:,1].reshape((n_verts1,1))
  1111. vz = np_verts1_uv[:,2].reshape((n_verts1,1))
  1112. co2 = np_lerp2(v00,v10,v01,v11,vx,vy)
  1113. n2 = np_lerp2(n00,n10,n01,n11,vx,vy)
  1114. if scale_mode == 'ADAPTIVE':
  1115. areas = np.array([[verts_area[v.index] for v in verts_v] for verts_v in verts])
  1116. a00 = areas[u,v].reshape((n_verts1,1))
  1117. a10 = areas[u1,v].reshape((n_verts1,1))
  1118. a01 = areas[u,v1].reshape((n_verts1,1))
  1119. a11 = areas[u1,v1].reshape((n_verts1,1))
  1120. # remapped z scale
  1121. a2 = np_lerp2(a00,a10,a01,a11,vx,vy)
  1122. co3 = co2 + n2 * vz * a2
  1123. else:
  1124. co3 = co2 + n2 * vz
  1125. coordinates = co3.flatten().tolist()
  1126. new_patch.data.vertices.foreach_set('co',coordinates)
  1127. # vertex groups
  1128. if bool_vertex_group:
  1129. for _weight, vg in zip(weight, new_patch.vertex_groups):
  1130. np_weight = np.array([[_weight[v.index] for v in verts_v] for verts_v in verts])
  1131. w00 = np_weight[u,v].reshape((n_verts1,1))
  1132. w10 = np_weight[u1,v].reshape((n_verts1,1))
  1133. w01 = np_weight[u,v1].reshape((n_verts1,1))
  1134. w11 = np_weight[u1,v1].reshape((n_verts1,1))
  1135. # remapped z scale
  1136. w2 = np_lerp2(w00,w10,w01,w11,vx,vy)
  1137. for vert_id in range(n_verts1):
  1138. vg.add([vert_id], w2[vert_id], "ADD")
  1139. if bool_shapekeys:
  1140. for i_sk, sk in enumerate(ob1.data.shape_keys.key_blocks):
  1141. np_verts1_uv = np.array(sk_uv[i_sk])
  1142. np_sk_uv_quads = np.array(sk_uv_quads[i_sk])
  1143. u = np_sk_uv_quads[:,0]
  1144. v = np_sk_uv_quads[:,1]
  1145. u1 = np_sk_uv_quads[:,2]
  1146. v1 = np_sk_uv_quads[:,3]
  1147. v00 = verts_xyz[u,v]
  1148. v10 = verts_xyz[u1,v]
  1149. v01 = verts_xyz[u,v1]
  1150. v11 = verts_xyz[u1,v1]
  1151. vx = np_verts1_uv[:,0].reshape((n_verts1,1))
  1152. vy = np_verts1_uv[:,1].reshape((n_verts1,1))
  1153. vz = np_verts1_uv[:,2].reshape((n_verts1,1))
  1154. co2 = np_lerp2(v00,v10,v01,v11,vx,vy)
  1155. n2 = np_lerp2(n00,n10,n01,n11,vx,vy)
  1156. if scale_mode == 'ADAPTIVE':
  1157. areas = np.array([[verts_area[v.index] for v in verts_v] for verts_v in verts])
  1158. a00 = areas[u,v].reshape((n_verts1,1))
  1159. a10 = areas[u1,v].reshape((n_verts1,1))
  1160. a01 = areas[u,v1].reshape((n_verts1,1))
  1161. a11 = areas[u1,v1].reshape((n_verts1,1))
  1162. # remapped z scale
  1163. a2 = np_lerp2(a00,a10,a01,a11,vx,vy)
  1164. co3 = co2 + n2 * vz * a2
  1165. else:
  1166. co3 = co2 + n2 * vz
  1167. coordinates = co3.flatten().tolist()
  1168. new_patch.data.shape_keys.key_blocks[sk.name].data.foreach_set('co', coordinates)
  1169. #new_patch.data.shape_keys.key_blocks[sk.name].data[i_vert].co = sk_co
  1170. else:
  1171. for _fvec, uv_quad, patch_vert in zip(verts1_uv, verts1_uv_quads, new_patch.data.vertices):
  1172. u = uv_quad[0]
  1173. v = uv_quad[1]
  1174. u1 = uv_quad[2]
  1175. v1 = uv_quad[3]
  1176. v00 = verts[u][v]
  1177. v10 = verts[u1][v]
  1178. v01 = verts[u][v1]
  1179. v11 = verts[u1][v1]
  1180. # interpolate Z scaling factor
  1181. fvec = _fvec.copy()
  1182. if scale_mode == 'ADAPTIVE':
  1183. a00 = verts_area[v00.index]
  1184. a10 = verts_area[v10.index]
  1185. a01 = verts_area[v01.index]
  1186. a11 = verts_area[v11.index]
  1187. fvec[2]*=lerp2(a00,a10,a01,a11,fvec)
  1188. # interpolate vertex on patch
  1189. patch_vert.co = lerp3(v00, v10, v01, v11, fvec)
  1190. # Vertex Group
  1191. if bool_vertex_group:
  1192. for _weight, vg in zip(weight, new_patch.vertex_groups):
  1193. w00 = _weight[v00.index]
  1194. w10 = _weight[v10.index]
  1195. w01 = _weight[v01.index]
  1196. w11 = _weight[v11.index]
  1197. wuv = lerp2(w00,w10,w01,w11, fvec)
  1198. vg.add([patch_vert.index], wuv, "ADD")
  1199. if bool_shapekeys:
  1200. for i_sk, sk in enumerate(ob1.data.shape_keys.key_blocks):
  1201. for i_vert, _fvec, _sk_uv_quad in zip(range(len(new_patch.data.vertices)), sk_uv[i_sk], sk_uv_quads[i_sk]):
  1202. u = _sk_uv_quad[0]
  1203. v = _sk_uv_quad[1]
  1204. u1 = _sk_uv_quad[2]
  1205. v1 = _sk_uv_quad[3]
  1206. v00 = verts[u][v]
  1207. v10 = verts[u1][v]
  1208. v01 = verts[u][v1]
  1209. v11 = verts[u1][v1]
  1210. fvec = _fvec.copy()
  1211. if scale_mode == 'ADAPTIVE':
  1212. a00 = verts_area[v00.index]
  1213. a10 = verts_area[v10.index]
  1214. a01 = verts_area[v01.index]
  1215. a11 = verts_area[v11.index]
  1216. fvec[2]*=lerp2(a00, a10, a01, a11, fvec)
  1217. sk_co = lerp3(v00, v10, v01, v11, fvec)
  1218. new_patch.data.shape_keys.key_blocks[sk.name].data[i_vert].co = sk_co
  1219. #if ob0.type == 'MESH': ob0.data = old_me0
  1220. if not bool_correct: return 0
  1221. bpy.ops.object.join()
  1222. if bool_shapekeys:
  1223. # set original values and combine Shape Keys and Vertex Groups
  1224. for sk, val in zip(_ob1.data.shape_keys.key_blocks, original_key_values):
  1225. sk.value = val
  1226. new_patch.data.shape_keys.key_blocks[sk.name].value = val
  1227. if bool_vertex_group:
  1228. for sk in new_patch.data.shape_keys.key_blocks:
  1229. for vg in new_patch.vertex_groups:
  1230. if sk.name == vg.name:
  1231. sk.vertex_group = vg.name
  1232. else:
  1233. try:
  1234. for sk, val in zip(_ob1.data.shape_keys.key_blocks, original_key_values):
  1235. sk.value = val
  1236. except: pass
  1237. new_name = ob0.name + "_" + ob1.name
  1238. new_patch.name = "tessellate_temp"
  1239. if bool_multires:
  1240. for m in ob0.modifiers:
  1241. if m.type == 'MULTIRES' and m.name == multires_name:
  1242. m.levels = levels
  1243. m.sculpt_levels = sculpt_levels
  1244. m.render_levels = render_levels
  1245. # restore original modifiers visibility for component object
  1246. try:
  1247. for m, vis in zip(_ob1.modifiers, mod_visibility):
  1248. m.show_viewport = vis
  1249. except: pass
  1250. bpy.data.objects.remove(before)
  1251. bpy.data.objects.remove(ob0)
  1252. bpy.data.objects.remove(ob1)
  1253. return new_patch
  1254. def tessellate_original(_ob0, _ob1, offset, zscale, gen_modifiers, com_modifiers, mode,
  1255. scale_mode, rotation_mode, rotation_shift, rotation_direction, rand_seed, fill_mode,
  1256. bool_vertex_group, bool_selection, bool_shapekeys,
  1257. bool_material_id, material_id, normals_mode, bounds_x, bounds_y):
  1258. if com_modifiers or _ob1.type != 'MESH': bool_shapekeys = False
  1259. random.seed(rand_seed)
  1260. if bool_shapekeys:
  1261. try:
  1262. original_key_values = []
  1263. for sk in _ob1.data.shape_keys.key_blocks:
  1264. original_key_values.append(sk.value)
  1265. sk.value = 0
  1266. except:
  1267. bool_shapekeys = False
  1268. if normals_mode == 'CUSTOM':
  1269. if _ob0.data.shape_keys != None:
  1270. ob0_sk = convert_object_to_mesh(_ob0, True, True)
  1271. me0_sk = ob0_sk.data
  1272. key_values0 = [sk.value for sk in _ob0.data.shape_keys.key_blocks]
  1273. for sk in _ob0.data.shape_keys.key_blocks: sk.value = 0
  1274. else: normals_mode == 'VERTS'
  1275. ob0 = convert_object_to_mesh(_ob0, gen_modifiers, True)
  1276. me0 = ob0.data
  1277. ob1 = convert_object_to_mesh(_ob1, com_modifiers, True)
  1278. me1 = ob1.data
  1279. # base normals
  1280. normals0 = []
  1281. if normals_mode == 'CUSTOM' and _ob0.data.shape_keys != None:
  1282. for sk, val in zip(_ob0.data.shape_keys.key_blocks, key_values0): sk.value = val
  1283. for v0, v1 in zip(me0.vertices, me0_sk.vertices):
  1284. normals0.append(v1.co - v0.co)
  1285. bpy.data.objects.remove(ob0_sk)
  1286. else:
  1287. me0.update()
  1288. normals0 = [v.normal for v in me0.vertices]
  1289. base_polygons = []
  1290. base_face_normals = []
  1291. n_faces0 = len(me0.polygons)
  1292. # Check if zero faces are selected
  1293. if (bool_selection and ob0.type == 'MESH') or bool_material_id:
  1294. for p in me0.polygons:
  1295. if (bool_selection and ob0.type == 'MESH'):
  1296. is_sel = p.select
  1297. else: is_sel = True
  1298. if bool_material_id:
  1299. is_mat = p.material_index == material_id
  1300. else: is_mat = True
  1301. if is_sel and is_mat:
  1302. base_polygons.append(p)
  1303. base_face_normals.append(p.normal)
  1304. else:
  1305. base_polygons = me0.polygons
  1306. base_face_normals = [p.normal for p in me0.polygons]
  1307. # numpy test: slower
  1308. #base_face_normals = np.zeros(n_faces0*3)
  1309. #me0.polygons.foreach_get("normal", base_face_normals)
  1310. #base_face_normals = base_face_normals.reshape((n_faces0,3))
  1311. if len(base_polygons) == 0:
  1312. bpy.data.objects.remove(ob0)
  1313. bpy.data.objects.remove(ob1)
  1314. bpy.data.meshes.remove(me1)
  1315. bpy.data.meshes.remove(me0)
  1316. return 0
  1317. if mode != 'BOUNDS':
  1318. bpy.ops.object.select_all(action='DESELECT')
  1319. for o in bpy.context.view_layer.objects: o.select_set(False)
  1320. bpy.context.view_layer.objects.active = ob1
  1321. ob1.select_set(True)
  1322. ob1.active_shape_key_index = 0
  1323. # Bound X
  1324. if bounds_x != 'EXTEND':
  1325. if mode == 'GLOBAL':
  1326. planes_co = ((0,0,0),(1,1,1))
  1327. plane_no = (1,0,0)
  1328. if mode == 'LOCAL':
  1329. planes_co = (ob1.matrix_world @ Vector((0,0,0)), ob1.matrix_world @ Vector((1,0,0)))
  1330. plane_no = planes_co[0]-planes_co[1]
  1331. bpy.ops.object.mode_set(mode='EDIT')
  1332. for co in planes_co:
  1333. bpy.ops.mesh.select_all(action='SELECT')
  1334. bpy.ops.mesh.bisect(plane_co=co, plane_no=plane_no)
  1335. bpy.ops.mesh.mark_seam()
  1336. bpy.ops.object.mode_set(mode='OBJECT')
  1337. _faces = ob1.data.polygons
  1338. if mode == 'GLOBAL':
  1339. for f in [f for f in _faces if (ob1.matrix_world @ f.center).x > 1]:
  1340. f.select = True
  1341. for f in [f for f in _faces if (ob1.matrix_world @ f.center).x < 0]:
  1342. f.select = True
  1343. else:
  1344. for f in [f for f in _faces if f.center.x > 1]:
  1345. f.select = True
  1346. for f in [f for f in _faces if f.center.x < 0]:
  1347. f.select = True
  1348. bpy.ops.object.mode_set(mode='EDIT')
  1349. bpy.ops.mesh.select_mode(type='FACE')
  1350. if bounds_x == 'CLIP':
  1351. bpy.ops.mesh.delete(type='FACE')
  1352. bpy.ops.object.mode_set(mode='OBJECT')
  1353. if bounds_x == 'CYCLIC':
  1354. bpy.ops.mesh.split()
  1355. bpy.ops.object.mode_set(mode='OBJECT')
  1356. # Bound Y
  1357. if bounds_y != 'EXTEND':
  1358. if mode == 'GLOBAL':
  1359. planes_co = ((0,0,0),(1,1,1))
  1360. plane_no = (0,1,0)
  1361. if mode == 'LOCAL':
  1362. planes_co = (ob1.matrix_world @ Vector((0,0,0)), ob1.matrix_world @ Vector((0,1,0)))
  1363. plane_no = planes_co[0]-planes_co[1]
  1364. bpy.ops.object.mode_set(mode='EDIT')
  1365. for co in planes_co:
  1366. bpy.ops.mesh.select_all(action='SELECT')
  1367. bpy.ops.mesh.bisect(plane_co=co, plane_no=plane_no)
  1368. bpy.ops.mesh.mark_seam()
  1369. bpy.ops.object.mode_set(mode='OBJECT')
  1370. _faces = ob1.data.polygons
  1371. if mode == 'GLOBAL':
  1372. for f in [f for f in _faces if (ob1.matrix_world @ f.center).y > 1]:
  1373. f.select = True
  1374. for f in [f for f in _faces if (ob1.matrix_world @ f.center).y < 0]:
  1375. f.select = True
  1376. else:
  1377. for f in [f for f in _faces if f.center.y > 1]:
  1378. f.select = True
  1379. for f in [f for f in _faces if f.center.y < 0]:
  1380. f.select = True
  1381. bpy.ops.object.mode_set(mode='EDIT')
  1382. bpy.ops.mesh.select_mode(type='FACE')
  1383. if bounds_y == 'CLIP':
  1384. bpy.ops.mesh.delete(type='FACE')
  1385. bpy.ops.object.mode_set(mode='OBJECT')
  1386. if bounds_y == 'CYCLIC':
  1387. bpy.ops.mesh.split()
  1388. bpy.ops.object.mode_set(mode='OBJECT')
  1389. bpy.ops.object.mode_set(mode='OBJECT')
  1390. #ob1 = new_ob1
  1391. me1 = ob1.data
  1392. verts0 = me0.vertices # Collect generator vertices
  1393. # Component statistics
  1394. n_verts1 = len(me1.vertices)
  1395. n_edges1 = len(me1.edges)
  1396. n_faces1 = len(me1.polygons)
  1397. # Create empty lists
  1398. new_verts = []
  1399. new_edges = []
  1400. new_faces = []
  1401. new_verts_np = np.array(())
  1402. # Component Coordinates
  1403. co1 = [0]*n_verts1*3
  1404. if mode == 'GLOBAL':
  1405. for v in me1.vertices:
  1406. v.co = ob1.matrix_world @ v.co
  1407. try:
  1408. for sk in me1.shape_keys.key_blocks:
  1409. sk.data[v.index].co = ob1.matrix_world @ sk.data[v.index].co
  1410. except: pass
  1411. if mode != 'BOUNDS':
  1412. if bounds_x == 'CYCLIC':
  1413. move_verts = []
  1414. for f in [f for f in me1.polygons if (f.center).x > 1]:
  1415. for v in f.vertices:
  1416. if v not in move_verts: move_verts.append(v)
  1417. for v in move_verts:
  1418. me1.vertices[v].co.x -= 1
  1419. try:
  1420. _ob1.active_shape_key_index = 0
  1421. for sk in me1.shape_keys.key_blocks:
  1422. sk.data[v].co.x -= 1
  1423. except: pass
  1424. move_verts = []
  1425. for f in [f for f in me1.polygons if (f.center).x < 0]:
  1426. for v in f.vertices:
  1427. if v not in move_verts: move_verts.append(v)
  1428. for v in move_verts:
  1429. me1.vertices[v].co.x += 1
  1430. try:
  1431. _ob1.active_shape_key_index = 0
  1432. for sk in me1.shape_keys.key_blocks:
  1433. sk.data[v].co.x += 1
  1434. except: pass
  1435. if bounds_y == 'CYCLIC':
  1436. move_verts = []
  1437. for f in [f for f in me1.polygons if (f.center).y > 1]:
  1438. for v in f.vertices:
  1439. if v not in move_verts: move_verts.append(v)
  1440. for v in move_verts:
  1441. me1.vertices[v].co.y -= 1
  1442. try:
  1443. #new_ob1.active_shape_key_index = 0
  1444. for sk in me1.shape_keys.key_blocks:
  1445. sk.data[v].co.y -= 1
  1446. except: pass
  1447. move_verts = []
  1448. for f in [f for f in me1.polygons if (f.center).y < 0]:
  1449. for v in f.vertices:
  1450. if v not in move_verts: move_verts.append(v)
  1451. for v in move_verts:
  1452. me1.vertices[v].co.y += 1
  1453. try:
  1454. #new_ob1.active_shape_key_index = 0
  1455. for sk in me1.shape_keys.key_blocks:
  1456. sk.data[v].co.y += 1
  1457. except: pass
  1458. if len(me1.vertices) == 0:
  1459. bpy.data.objects.remove(ob0)
  1460. bpy.data.objects.remove(ob1)
  1461. return 0
  1462. me1.vertices.foreach_get("co", co1)
  1463. co1 = np.array(co1)
  1464. vx = co1[0::3].reshape((n_verts1,1))
  1465. vy = co1[1::3].reshape((n_verts1,1))
  1466. vz = co1[2::3].reshape((n_verts1,1))
  1467. min_c = Vector((vx.min(), vy.min(), vz.min())) # Min BB Corner
  1468. max_c = Vector((vx.max(), vy.max(), vz.max())) # Max BB Corner
  1469. bb = max_c - min_c # Bounding Box
  1470. # Component Coordinates
  1471. if mode == 'BOUNDS':
  1472. vx = (vx - min_c[0]) / bb[0] if bb[0] != 0 else 0.5
  1473. vy = (vy - min_c[1]) / bb[1] if bb[1] != 0 else 0.5
  1474. vz = (vz - min_c[2]) / bb[2] if bb[2] != 0 else 0
  1475. vz = (vz - 0.5 + offset * 0.5) * zscale
  1476. #vz = ((vz - min_c[2]) + (-0.5 + offset * 0.5) * bb[2]) * zscale
  1477. else:
  1478. vz *= zscale
  1479. # Component polygons
  1480. fs1 = [[i for i in p.vertices] for p in me1.polygons]
  1481. new_faces = fs1[:]
  1482. # Component edges
  1483. es1 = np.array([[i for i in e.vertices] for e in me1.edges])
  1484. #es1 = [[i for i in e.vertices] for e in me1.edges if e.is_loose]
  1485. new_edges = es1[:]
  1486. # SHAPE KEYS
  1487. if bool_shapekeys:
  1488. basis = True #com_modifiers
  1489. vx_key = []
  1490. vy_key = []
  1491. vz_key = []
  1492. sk_np = []
  1493. for sk in ob1.data.shape_keys.key_blocks:
  1494. do_shapekeys = True
  1495. # set all keys to 0
  1496. for _sk in ob1.data.shape_keys.key_blocks: _sk.value = 0
  1497. sk.value = 1
  1498. if basis:
  1499. basis = False
  1500. continue
  1501. # Apply component modifiers
  1502. if com_modifiers:
  1503. sk_ob = convert_object_to_mesh(_ob1)
  1504. sk_data = sk_ob.data
  1505. source = sk_data.vertices
  1506. else:
  1507. source = sk.data
  1508. shapekeys = []
  1509. for v in source:
  1510. if mode == 'BOUNDS':
  1511. vert = v.co - min_c
  1512. vert[0] = (vert[0] / bb[0] if bb[0] != 0 else 0.5)
  1513. vert[1] = (vert[1] / bb[1] if bb[1] != 0 else 0.5)
  1514. vert[2] = (vert[2] / bb[2] if bb[2] != 0 else vert[2])
  1515. vert[2] = (vert[2] - 0.5 + offset * 0.5) * zscale
  1516. elif mode == 'LOCAL':
  1517. vert = v.co.xyz
  1518. vert[2] *= zscale
  1519. #vert[2] = (vert[2] - min_c[2] + (-0.5 + offset * 0.5) * bb[2]) * \
  1520. # zscale
  1521. elif mode == 'GLOBAL':
  1522. vert = v.co.xyz
  1523. #vert = ob1.matrix_world @ v.co
  1524. vert[2] *= zscale
  1525. shapekeys.append(vert)
  1526. # Component vertices
  1527. key1 = np.array([v for v in shapekeys]).reshape(len(shapekeys), 3, 1)
  1528. vx_key.append(key1[:, 0])
  1529. vy_key.append(key1[:, 1])
  1530. vz_key.append(key1[:, 2])
  1531. #sk_np.append([])
  1532. # All vertex group
  1533. if bool_vertex_group or rotation_mode == 'WEIGHT':
  1534. try:
  1535. weight = []
  1536. for vg in ob0.vertex_groups:
  1537. _weight = []
  1538. for i,v in enumerate(me0.vertices):
  1539. try:
  1540. _weight.append(vg.weight(i))
  1541. except:
  1542. _weight.append(0)
  1543. weight.append(_weight)
  1544. except:
  1545. bool_vertex_group = False
  1546. # Adaptive Z
  1547. if scale_mode == 'ADAPTIVE':
  1548. com_area = bb[0]*bb[1]
  1549. if mode != 'BOUNDS' or com_area == 0: com_area = 1
  1550. verts_area = []
  1551. bm = bmesh.new()
  1552. bm.from_mesh(me0)
  1553. bm.verts.ensure_lookup_table()
  1554. for v in bm.verts:
  1555. area = 0
  1556. faces = v.link_faces
  1557. for f in faces:
  1558. area += f.calc_area()
  1559. try:
  1560. area/=len(faces) # average area
  1561. area/=com_area
  1562. verts_area.append(sqrt(area)*bb[2])
  1563. #verts_area.append(area)
  1564. except:
  1565. verts_area.append(1)
  1566. count = 0 # necessary for UV calculation
  1567. # TESSELLATION
  1568. j = 0
  1569. jj = -1
  1570. bool_correct = False
  1571. # optimization test
  1572. n_faces = len(base_polygons)
  1573. _vs0 = [0]*n_faces
  1574. _nvs0 = [0]*n_faces
  1575. _sz = [0]*n_faces
  1576. n_vg = len(ob0.vertex_groups)
  1577. _w0 = [[0]*n_faces for i in range(n_vg)]
  1578. np_faces = [np.array(p) for p in fs1]
  1579. new_faces = [0]*n_faces*n_faces1
  1580. face1_count = 0
  1581. for j, p in enumerate(base_polygons):
  1582. bool_correct = True
  1583. if rotation_mode in ['UV', 'WEIGHT'] and ob0.type != 'MESH':
  1584. rotation_mode = 'DEFAULT'
  1585. ordered = p.vertices
  1586. # Random rotation
  1587. if rotation_mode == 'RANDOM':
  1588. shifted_vertices = []
  1589. n_poly_verts = len(p.vertices)
  1590. rand = random.randint(0, n_poly_verts)
  1591. for i in range(n_poly_verts):
  1592. shifted_vertices.append(p.vertices[(i + rand) % n_poly_verts])
  1593. if scale_mode == 'ADAPTIVE':
  1594. verts_area0 = np.array([verts_area[i] for i in shifted_vertices])
  1595. ordered = shifted_vertices
  1596. # UV rotation
  1597. elif rotation_mode == 'UV':
  1598. if len(ob0.data.uv_layers) > 0:
  1599. i = p.index
  1600. if bool_material_id:
  1601. count = sum([len(p.vertices) for p in me0.polygons[:i]])
  1602. #if i == 0: count = 0
  1603. v01 = (me0.uv_layers.active.data[count].uv +
  1604. me0.uv_layers.active.data[count + 1].uv)
  1605. if len(p.vertices) > 3:
  1606. v32 = (me0.uv_layers.active.data[count + 3].uv +
  1607. me0.uv_layers.active.data[count + 2].uv)
  1608. else:
  1609. v32 = (me0.uv_layers.active.data[count].uv +
  1610. me0.uv_layers.active.data[count + 2].uv)
  1611. v0132 = v32 - v01
  1612. v0132.normalize()
  1613. v12 = (me0.uv_layers.active.data[count + 1].uv +
  1614. me0.uv_layers.active.data[count + 2].uv)
  1615. if len(p.vertices) > 3:
  1616. v03 = (me0.uv_layers.active.data[count].uv +
  1617. me0.uv_layers.active.data[count + 3].uv)
  1618. else:
  1619. v03 = (me0.uv_layers.active.data[count].uv +
  1620. me0.uv_layers.active.data[count].uv)
  1621. v1203 = v03 - v12
  1622. v1203.normalize()
  1623. vertUV = []
  1624. dot1203 = v1203.x
  1625. dot0132 = v0132.x
  1626. if(abs(dot1203) < abs(dot0132)):
  1627. if (dot0132 > 0):
  1628. vertUV = p.vertices[1:] + p.vertices[:1]
  1629. else:
  1630. vertUV = p.vertices[3:] + p.vertices[:3]
  1631. else:
  1632. if(dot1203 < 0):
  1633. vertUV = p.vertices[:]
  1634. else:
  1635. vertUV = p.vertices[2:] + p.vertices[:2]
  1636. ordered = vertUV
  1637. count += len(p.vertices)
  1638. # Weight Rotation
  1639. elif rotation_mode == 'WEIGHT':
  1640. if len(weight) > 0:
  1641. active_weight = weight[ob0.vertex_groups.active_index]
  1642. i = p.index
  1643. face_weights = [active_weight[v] for v in p.vertices]
  1644. face_weights*=2
  1645. if rotation_direction == 'DIAG':
  1646. differential = [face_weights[ii]-face_weights[ii+2] for ii in range(4)]
  1647. else:
  1648. differential = [face_weights[ii]+face_weights[ii+1]-face_weights[ii+2]- face_weights[ii+3] for ii in range(4)]
  1649. starting = differential.index(max(differential))
  1650. ordered = p.vertices[starting:] + p.vertices[:starting]
  1651. if rotation_mode != 'RANDOM':
  1652. ordered = np.roll(np.array(ordered),rotation_shift)
  1653. ordered = np.array((ordered[0], ordered[1], ordered[2], ordered[-1]))
  1654. # assign vertices and values
  1655. vs0 = np.array([verts0[i].co for i in ordered])
  1656. #nvs0 = np.array([verts0[i].normal for i in ordered])
  1657. nvs0 = np.array([normals0[i] for i in ordered])
  1658. if scale_mode == 'ADAPTIVE':
  1659. np_verts_area = np.array([verts_area[i] for i in ordered])
  1660. _sz[j] = np_verts_area
  1661. # Vertex weight
  1662. if bool_vertex_group:
  1663. ws0 = []
  1664. for w in weight:
  1665. _ws0 = []
  1666. for i in ordered:
  1667. try:
  1668. _ws0.append(w[i])
  1669. except:
  1670. _ws0.append(0)
  1671. ws0.append(np.array(_ws0))
  1672. # optimization test
  1673. _vs0[j] = (vs0[0], vs0[1], vs0[2], vs0[-1])
  1674. if normals_mode != 'FACES':
  1675. _nvs0[j] = (nvs0[0], nvs0[1], nvs0[2], nvs0[-1])
  1676. if bool_vertex_group:
  1677. for i_vg, ws0_face in enumerate(ws0):
  1678. _w0[i_vg][j] = (ws0_face[0], ws0_face[1], ws0_face[2], ws0_face[-1])
  1679. for p in fs1:
  1680. new_faces[face1_count] = [i + n_verts1 * j for i in p]
  1681. face1_count += 1
  1682. # build edges list
  1683. n_edges1 = new_edges.shape[0]
  1684. new_edges = new_edges.reshape((1, n_edges1, 2))
  1685. new_edges = new_edges.repeat(n_faces,axis=0)
  1686. new_edges = new_edges.reshape((n_edges1*n_faces, 2))
  1687. increment = np.arange(n_faces)*n_verts1
  1688. increment = increment.repeat(n_edges1, axis=0)
  1689. increment = increment.reshape((n_faces*n_edges1,1))
  1690. new_edges = new_edges + increment
  1691. # optimization test
  1692. _vs0 = np.array(_vs0)
  1693. _sz = np.array(_sz)
  1694. _vs0_0 = _vs0[:,0].reshape((n_faces,1,3))
  1695. _vs0_1 = _vs0[:,1].reshape((n_faces,1,3))
  1696. _vs0_2 = _vs0[:,2].reshape((n_faces,1,3))
  1697. _vs0_3 = _vs0[:,3].reshape((n_faces,1,3))
  1698. # remapped vertex coordinates
  1699. v2 = np_lerp2(_vs0_0, _vs0_1, _vs0_3, _vs0_2, vx, vy)
  1700. # remapped vertex normal
  1701. if normals_mode != 'FACES':
  1702. _nvs0 = np.array(_nvs0)
  1703. _nvs0_0 = _nvs0[:,0].reshape((n_faces,1,3))
  1704. _nvs0_1 = _nvs0[:,1].reshape((n_faces,1,3))
  1705. _nvs0_2 = _nvs0[:,2].reshape((n_faces,1,3))
  1706. _nvs0_3 = _nvs0[:,3].reshape((n_faces,1,3))
  1707. nv2 = np_lerp2(_nvs0_0, _nvs0_1, _nvs0_3, _nvs0_2, vx, vy)
  1708. else:
  1709. nv2 = np.array(base_face_normals).reshape((n_faces,1,3))
  1710. # interpolate vertex groups
  1711. if bool_vertex_group:
  1712. w = np.array(_w0)
  1713. w_0 = w[:,:,0].reshape((n_vg, n_faces,1,1))
  1714. w_1 = w[:,:,1].reshape((n_vg, n_faces,1,1))
  1715. w_2 = w[:,:,2].reshape((n_vg, n_faces,1,1))
  1716. w_3 = w[:,:,3].reshape((n_vg, n_faces,1,1))
  1717. # remapped weight
  1718. w = np_lerp2(w_0, w_1, w_3, w_2, vx, vy)
  1719. w = w.reshape((n_vg, n_faces*n_verts1))
  1720. if scale_mode == 'ADAPTIVE':
  1721. _sz_0 = _sz[:,0].reshape((n_faces,1,1))
  1722. _sz_1 = _sz[:,1].reshape((n_faces,1,1))
  1723. _sz_2 = _sz[:,2].reshape((n_faces,1,1))
  1724. _sz_3 = _sz[:,3].reshape((n_faces,1,1))
  1725. # remapped z scale
  1726. sz2 = np_lerp2(_sz_0, _sz_1, _sz_3, _sz_2, vx, vy)
  1727. v3 = v2 + nv2 * vz * sz2
  1728. else:
  1729. v3 = v2 + nv2 * vz
  1730. new_verts_np = v3.reshape((n_faces*n_verts1,3))
  1731. if bool_shapekeys:
  1732. n_sk = len(vx_key)
  1733. sk_np = [0]*n_sk
  1734. for i in range(n_sk):
  1735. vx = np.array(vx_key[i])
  1736. vy = np.array(vy_key[i])
  1737. vz = np.array(vz_key[i])
  1738. # remapped vertex coordinates
  1739. v2 = np_lerp2(_vs0_0, _vs0_1, _vs0_3, _vs0_2, vx, vy)
  1740. # remapped vertex normal
  1741. if normals_mode != 'FACES':
  1742. nv2 = np_lerp2(_nvs0_0, _nvs0_1, _nvs0_3, _nvs0_2, vx, vy)
  1743. else:
  1744. nv2 = np.array(base_face_normals).reshape((n_faces,1,3))
  1745. if scale_mode == 'ADAPTIVE':
  1746. # remapped z scale
  1747. sz2 = np_lerp2(_sz_0, _sz_1, _sz_3, _sz_2, vx, vy)
  1748. v3 = v2 + nv2 * vz * sz2
  1749. else:
  1750. v3 = v2 + nv2 * vz
  1751. sk_np[i] = v3.reshape((n_faces*n_verts1,3))
  1752. #if ob0.type == 'MESH': ob0.data = old_me0
  1753. if not bool_correct:
  1754. #bpy.data.objects.remove(ob1)
  1755. return 0
  1756. new_verts = new_verts_np.tolist()
  1757. new_name = ob0.name + "_" + ob1.name
  1758. new_me = bpy.data.meshes.new(new_name)
  1759. new_me.from_pydata(new_verts, new_edges.tolist(), new_faces)
  1760. new_me.update(calc_edges=True)
  1761. new_ob = bpy.data.objects.new("tessellate_temp", new_me)
  1762. # vertex group
  1763. if bool_vertex_group and False:
  1764. for vg in ob0.vertex_groups:
  1765. new_ob.vertex_groups.new(name=vg.name)
  1766. for i in range(len(vg_np[vg.index])):
  1767. new_ob.vertex_groups[vg.name].add([i], vg_np[vg.index][i],"ADD")
  1768. # vertex group
  1769. if bool_vertex_group:
  1770. for vg in ob0.vertex_groups:
  1771. new_ob.vertex_groups.new(name=vg.name)
  1772. for i, vertex_weight in enumerate(w[vg.index]):
  1773. new_ob.vertex_groups[vg.name].add([i], vertex_weight,"ADD")
  1774. if bool_shapekeys:
  1775. basis = com_modifiers
  1776. sk_count = 0
  1777. for sk, val in zip(_ob1.data.shape_keys.key_blocks, original_key_values):
  1778. sk.value = val
  1779. new_ob.shape_key_add(name=sk.name, from_mix=False)
  1780. new_ob.data.shape_keys.key_blocks[sk.name].value = val
  1781. # set shape keys vertices
  1782. sk_data = new_ob.data.shape_keys.key_blocks[sk.name].data
  1783. if sk_count == 0:
  1784. sk_count += 1
  1785. continue
  1786. for id in range(len(sk_data)):
  1787. sk_data[id].co = sk_np[sk_count-1][id]
  1788. sk_count += 1
  1789. if bool_vertex_group:
  1790. for sk in new_ob.data.shape_keys.key_blocks:
  1791. for vg in new_ob.vertex_groups:
  1792. if sk.name == vg.name:
  1793. sk.vertex_group = vg.name
  1794. # EDGES SEAMS
  1795. edge_data = [0]*n_edges1
  1796. me1.edges.foreach_get("use_seam",edge_data)
  1797. if any(edge_data):
  1798. edge_data = edge_data*n_faces
  1799. new_ob.data.edges.foreach_set("use_seam",edge_data)
  1800. # EDGES SHARP
  1801. edge_data = [0]*n_edges1
  1802. me1.edges.foreach_get("use_edge_sharp",edge_data)
  1803. if any(edge_data):
  1804. edge_data = edge_data*n_faces
  1805. new_ob.data.edges.foreach_set("use_edge_sharp",edge_data)
  1806. bpy.ops.object.select_all(action='DESELECT')
  1807. bpy.context.collection.objects.link(new_ob)
  1808. new_ob.select_set(True)
  1809. bpy.context.view_layer.objects.active = new_ob
  1810. # EDGES BEVEL
  1811. edge_data = [0]*n_edges1
  1812. me1.edges.foreach_get("bevel_weight",edge_data)
  1813. if any(edge_data):
  1814. bpy.ops.object.mode_set(mode='EDIT')
  1815. bpy.ops.mesh.select_all(action='SELECT')
  1816. bpy.ops.transform.edge_bevelweight(value=1)
  1817. bpy.ops.object.mode_set(mode='OBJECT')
  1818. edge_data = edge_data*n_faces
  1819. new_ob.data.edges.foreach_set("bevel_weight",edge_data)
  1820. # EDGE CREASES
  1821. edge_data = [0]*n_edges1
  1822. me1.edges.foreach_get("crease",edge_data)
  1823. if any(edge_data):
  1824. bpy.ops.object.mode_set(mode='EDIT')
  1825. bpy.ops.mesh.select_all(action='SELECT')
  1826. bpy.ops.transform.edge_crease(value=1)
  1827. bpy.ops.object.mode_set(mode='OBJECT')
  1828. edge_data = edge_data*n_faces
  1829. new_ob.data.edges.foreach_set('crease', edge_data)
  1830. # MATERIALS
  1831. for slot in ob1.material_slots: new_ob.data.materials.append(slot.material)
  1832. polygon_materials = [0]*n_faces1
  1833. me1.polygons.foreach_get("material_index", polygon_materials)
  1834. polygon_materials *= n_faces
  1835. new_ob.data.polygons.foreach_set("material_index", polygon_materials)
  1836. new_ob.data.update() ###
  1837. try:
  1838. bpy.data.objects.remove(new_ob1)
  1839. except: pass
  1840. bpy.data.objects.remove(ob0)
  1841. bpy.data.meshes.remove(me0)
  1842. bpy.data.objects.remove(ob1)
  1843. bpy.data.meshes.remove(me1)
  1844. return new_ob
  1845. class tissue_tessellate(Operator):
  1846. bl_idname = "object.tissue_tessellate"
  1847. bl_label = "Tessellate"
  1848. bl_description = ("Create a copy of selected object on the active object's "
  1849. "faces, adapting the shape to the different faces")
  1850. bl_options = {'REGISTER', 'UNDO'}
  1851. bool_hold : BoolProperty(
  1852. name="Hold",
  1853. description="Wait...",
  1854. default=False
  1855. )
  1856. bool_lock : BoolProperty(
  1857. name="Lock",
  1858. description="Prevent automatic update on settings changes or if other objects have it in the hierarchy.",
  1859. default=False
  1860. )
  1861. bool_dependencies : BoolProperty(
  1862. name="Update Dependencies",
  1863. description="Automatically updates base and components as well, if results of other tessellations",
  1864. default=False
  1865. )
  1866. object_name : StringProperty(
  1867. name="",
  1868. description="Name of the generated object"
  1869. )
  1870. zscale : FloatProperty(
  1871. name="Scale",
  1872. default=1,
  1873. soft_min=0,
  1874. soft_max=10,
  1875. description="Scale factor for the component thickness"
  1876. )
  1877. scale_mode : EnumProperty(
  1878. items=(
  1879. ('CONSTANT', "Constant", "Uniform thickness"),
  1880. ('ADAPTIVE', "Relative", "Preserve component's proportions")
  1881. ),
  1882. default='ADAPTIVE',
  1883. name="Z-Scale according to faces size"
  1884. )
  1885. offset : FloatProperty(
  1886. name="Surface Offset",
  1887. default=1,
  1888. min=-1, max=1,
  1889. soft_min=-1,
  1890. soft_max=1,
  1891. description="Surface offset"
  1892. )
  1893. mode : EnumProperty(
  1894. items=(
  1895. ('BOUNDS', "Bounds", "The component fits automatically the size of the target face"),
  1896. ('LOCAL', "Local", "Based on Local coordinates, from 0 to 1"),
  1897. ('GLOBAL', 'Global', "Based on Global coordinates, from 0 to 1")),
  1898. default='BOUNDS',
  1899. name="Component Mode"
  1900. )
  1901. rotation_mode : EnumProperty(
  1902. items=(('RANDOM', "Random", "Random faces rotation"),
  1903. ('UV', "Active UV", "Face rotation is based on UV coordinates"),
  1904. ('WEIGHT', "Active Weight", "Rotate according to Vertex Group gradient"),
  1905. ('DEFAULT', "Default", "Default rotation")),
  1906. default='DEFAULT',
  1907. name="Component Rotation"
  1908. )
  1909. rotation_direction : EnumProperty(
  1910. items=(('ORTHO', "Orthogonal", "Component main directions in XY"),
  1911. ('DIAG', "Diagonal", "Component main direction aligned with diagonal")),
  1912. default='ORTHO',
  1913. name="Direction"
  1914. )
  1915. rotation_shift : IntProperty(
  1916. name="Shift",
  1917. default=0,
  1918. soft_min=0,
  1919. soft_max=3,
  1920. description="Shift components rotation"
  1921. )
  1922. fill_mode : EnumProperty(
  1923. items=(
  1924. ('QUAD', 'Quad', 'Regular quad tessellation. Uses only 3 or 4 vertices'),
  1925. ('FAN', 'Fan', 'Radial tessellation for polygonal faces'),
  1926. ('PATCH', 'Patch', 'Curved tessellation according to the last ' +
  1927. 'Subsurf\n(or Multires) modifiers. Works only with 4 sides ' +
  1928. 'patches.\nAfter the last Subsurf (or Multires) only ' +
  1929. 'deformation\nmodifiers can be used'),
  1930. ('FRAME', 'Frame', 'Essellation along the edges of each face')),
  1931. default='QUAD',
  1932. name="Fill Mode"
  1933. )
  1934. combine_mode : EnumProperty(
  1935. items=(
  1936. ('LAST', 'Last', 'Show only the last iteration'),
  1937. ('UNUSED', 'Unused', 'Combine each iteration with the unused faces of the previous iteration. Used for branching systems'),
  1938. ('ALL', 'All', 'Combine the result of all iterations')),
  1939. default='LAST',
  1940. name="Combine Mode",
  1941. )
  1942. gen_modifiers : BoolProperty(
  1943. name="Generator Modifiers",
  1944. default=False,
  1945. description="Apply Modifiers and Shape Keys to the base object"
  1946. )
  1947. com_modifiers : BoolProperty(
  1948. name="Component Modifiers",
  1949. default=False,
  1950. description="Apply Modifiers and Shape Keys to the component object"
  1951. )
  1952. merge : BoolProperty(
  1953. name="Merge",
  1954. default=False,
  1955. description="Merge vertices in adjacent duplicates"
  1956. )
  1957. merge_thres : FloatProperty(
  1958. name="Distance",
  1959. default=0.001,
  1960. soft_min=0,
  1961. soft_max=10,
  1962. description="Limit below which to merge vertices"
  1963. )
  1964. bool_random : BoolProperty(
  1965. name="Randomize",
  1966. default=False,
  1967. description="Randomize component rotation"
  1968. )
  1969. random_seed : IntProperty(
  1970. name="Seed",
  1971. default=0,
  1972. soft_min=0,
  1973. soft_max=10,
  1974. description="Random seed"
  1975. )
  1976. bool_vertex_group : BoolProperty(
  1977. name="Map Vertex Groups",
  1978. default=False,
  1979. description="Transfer all Vertex Groups from Base object"
  1980. )
  1981. bool_selection : BoolProperty(
  1982. name="On selected Faces",
  1983. default=False,
  1984. description="Create Tessellation only on selected faces"
  1985. )
  1986. bool_shapekeys : BoolProperty(
  1987. name="Use Shape Keys",
  1988. default=False,
  1989. description="Transfer Component's Shape Keys. If the name of Vertex "
  1990. "Groups and Shape Keys are the same, they will be "
  1991. "automatically combined"
  1992. )
  1993. bool_smooth : BoolProperty(
  1994. name="Smooth Shading",
  1995. default=False,
  1996. description="Output faces with smooth shading rather than flat shaded"
  1997. )
  1998. bool_materials : BoolProperty(
  1999. name="Transfer Materials",
  2000. default=True,
  2001. description="Preserve component's materials"
  2002. )
  2003. generator : StringProperty(
  2004. name="",
  2005. description="Base object for the tessellation",
  2006. default = ""
  2007. )
  2008. component : StringProperty(
  2009. name="",
  2010. description="Component object for the tessellation",
  2011. default = ""
  2012. )
  2013. bool_material_id : BoolProperty(
  2014. name="Tessellation on Material ID",
  2015. default=False,
  2016. description="Apply the component only on the selected Material"
  2017. )
  2018. bool_dissolve_seams : BoolProperty(
  2019. name="Dissolve Seams",
  2020. default=False,
  2021. description="Dissolve all seam edges"
  2022. )
  2023. material_id : IntProperty(
  2024. name="Material ID",
  2025. default=0,
  2026. min=0,
  2027. description="Material ID"
  2028. )
  2029. iterations : IntProperty(
  2030. name="Iterations",
  2031. default=1,
  2032. min=1,
  2033. soft_max=5,
  2034. description="Automatically repeat the Tessellation using the "
  2035. + "generated geometry as new base object.\nUsefull for "
  2036. + "for branching systems. Dangerous!"
  2037. )
  2038. bool_combine : BoolProperty(
  2039. name="Combine unused",
  2040. default=False,
  2041. description="Combine the generated geometry with unused faces"
  2042. )
  2043. bool_advanced : BoolProperty(
  2044. name="Advanced Settings",
  2045. default=False,
  2046. description="Show more settings"
  2047. )
  2048. normals_mode : EnumProperty(
  2049. items=(
  2050. ('VERTS', 'Normals', 'Consistent direction based on vertices normal'),
  2051. ('FACES', 'Individual Faces', 'Based on individual faces normal'),
  2052. ('CUSTOM', 'Custom', "According to Base object's shape keys")),
  2053. default='VERTS',
  2054. name="Direction"
  2055. )
  2056. bool_multi_components : BoolProperty(
  2057. name="Multi Components",
  2058. default=False,
  2059. description="Combine different components according to materials name"
  2060. )
  2061. bounds_x : EnumProperty(
  2062. items=(
  2063. ('EXTEND', 'Extend', 'Default X coordinates'),
  2064. ('CLIP', 'Clip', 'Trim out of bounds in X direction'),
  2065. ('CYCLIC', 'Cyclic', 'Cyclic components in X direction')),
  2066. default='EXTEND',
  2067. name="Bounds X",
  2068. )
  2069. bounds_y : EnumProperty(
  2070. items=(
  2071. ('EXTEND', 'Extend', 'Default Y coordinates'),
  2072. ('CLIP', 'Clip', 'Trim out of bounds in Y direction'),
  2073. ('CYCLIC', 'Cyclic', 'Cyclic components in Y direction')),
  2074. default='EXTEND',
  2075. name="Bounds Y",
  2076. )
  2077. close_mesh : EnumProperty(
  2078. items=(
  2079. ('NONE', 'None', 'Keep the mesh open'),
  2080. ('CAP', 'Cap Holes', 'Automatically cap open loops'),
  2081. ('BRIDGE', 'Bridge Loops', 'Automatically bridge loop pairs')),
  2082. default='NONE',
  2083. name="Close Mesh"
  2084. )
  2085. cap_faces : BoolProperty(
  2086. name="Cap Holes",
  2087. default=False,
  2088. description="Cap open edges loops"
  2089. )
  2090. frame_boundary : BoolProperty(
  2091. name="Frame Boundary",
  2092. default=False,
  2093. description="Support face boundaries"
  2094. )
  2095. fill_frame : BoolProperty(
  2096. name="Fill Frame",
  2097. default=False,
  2098. description="Fill inner faces with Fan tessellation"
  2099. )
  2100. frame_boundary_mat : IntProperty(
  2101. name="Material Offset",
  2102. default=0,
  2103. description="Material Offset for boundaries"
  2104. )
  2105. fill_frame_mat : IntProperty(
  2106. name="Material Offset",
  2107. default=0,
  2108. description="Material Offset for inner faces"
  2109. )
  2110. open_edges_crease : FloatProperty(
  2111. name="Open Edges Crease",
  2112. default=0,
  2113. min=0,
  2114. max=1,
  2115. description="Automatically set crease for open edges"
  2116. )
  2117. bridge_smoothness : FloatProperty(
  2118. name="Smoothness",
  2119. default=1,
  2120. min=0,
  2121. max=1,
  2122. description="Bridge Smoothness"
  2123. )
  2124. frame_thickness : FloatProperty(
  2125. name="Frame Thickness",
  2126. default=0.2,
  2127. min=0,
  2128. soft_max=2,
  2129. description="Frame Thickness"
  2130. )
  2131. frame_mode : EnumProperty(
  2132. items=(
  2133. ('CONSTANT', 'Constant', 'Even thickness'),
  2134. ('RELATIVE', 'Relative', 'Frame offset depends on face areas')),
  2135. default='CONSTANT',
  2136. name="Offset"
  2137. )
  2138. bridge_cuts : IntProperty(
  2139. name="Cuts",
  2140. default=0,
  2141. min=0,
  2142. max=20,
  2143. description="Bridge Cuts"
  2144. )
  2145. cap_material_index : IntProperty(
  2146. name="Material",
  2147. default=0,
  2148. min=0,
  2149. description="Material index for the cap/bridge faces"
  2150. )
  2151. patch_subs : IntProperty(
  2152. name="Patch Subdivisions",
  2153. default=1,
  2154. min=0,
  2155. description="Subdivisions levels for Patch tessellation after the first iteration"
  2156. )
  2157. working_on = ""
  2158. def draw(self, context):
  2159. allowed_obj = ('MESH', 'CURVE', 'SURFACE', 'FONT', 'META')
  2160. '''
  2161. try:
  2162. bool_working = self.working_on == self.object_name and \
  2163. self.working_on != ""
  2164. except:
  2165. bool_working = False
  2166. '''
  2167. bool_working = False
  2168. bool_allowed = False
  2169. ob0 = None
  2170. ob1 = None
  2171. sel = bpy.context.selected_objects
  2172. if len(sel) == 1:
  2173. try:
  2174. ob0 = sel[0].tissue_tessellate.generator
  2175. ob1 = sel[0].tissue_tessellate.component
  2176. self.generator = ob0.name
  2177. self.component = ob1.name
  2178. if self.working_on == '':
  2179. load_parameters(self,sel[0])
  2180. self.working_on = sel[0].name
  2181. bool_working = True
  2182. bool_allowed = True
  2183. except:
  2184. pass
  2185. if len(sel) == 2:
  2186. bool_allowed = True
  2187. for o in sel:
  2188. if o.type not in allowed_obj:
  2189. bool_allowed = False
  2190. if len(sel) != 2 and not bool_working:
  2191. layout = self.layout
  2192. layout.label(icon='INFO')
  2193. layout.label(text="Please, select two different objects")
  2194. layout.label(text="Select first the Component object, then select")
  2195. layout.label(text="the Base object.")
  2196. elif not bool_allowed and not bool_working:
  2197. layout = self.layout
  2198. layout.label(icon='INFO')
  2199. layout.label(text="Only Mesh, Curve, Surface or Text objects are allowed")
  2200. else:
  2201. if ob0 == ob1 == None:
  2202. ob0 = bpy.context.active_object
  2203. self.generator = ob0.name
  2204. for o in sel:
  2205. if o != ob0:
  2206. ob1 = o
  2207. self.component = o.name
  2208. self.no_component = False
  2209. break
  2210. # new object name
  2211. if self.object_name == "":
  2212. if self.generator == "":
  2213. self.object_name = "Tessellation"
  2214. else:
  2215. #self.object_name = self.generator + "_Tessellation"
  2216. self.object_name = "Tessellation"
  2217. layout = self.layout
  2218. # Base and Component
  2219. col = layout.column(align=True)
  2220. row = col.row(align=True)
  2221. row.label(text="BASE : " + self.generator)
  2222. row.label(text="COMPONENT : " + self.component)
  2223. # Base Modifiers
  2224. row = col.row(align=True)
  2225. col2 = row.column(align=True)
  2226. col2.prop(self, "gen_modifiers", text="Use Modifiers", icon='MODIFIER')
  2227. base = bpy.data.objects[self.generator]
  2228. try:
  2229. if not (base.modifiers or base.data.shape_keys):
  2230. col2.enabled = False
  2231. self.gen_modifiers = False
  2232. except:
  2233. col2.enabled = False
  2234. self.gen_modifiers = False
  2235. # Component Modifiers
  2236. row.separator()
  2237. col3 = row.column(align=True)
  2238. col3.prop(self, "com_modifiers", text="Use Modifiers", icon='MODIFIER')
  2239. component = bpy.data.objects[self.component]
  2240. try:
  2241. if not (component.modifiers or component.data.shape_keys):
  2242. col3.enabled = False
  2243. self.com_modifiers = False
  2244. except:
  2245. col3.enabled = False
  2246. self.com_modifiers = False
  2247. col.separator()
  2248. # Fill and Rotation
  2249. row = col.row(align=True)
  2250. row.label(text="Fill Mode:")
  2251. row = col.row(align=True)
  2252. row.prop(
  2253. self, "fill_mode", icon='NONE', expand=True,
  2254. slider=True, toggle=False, icon_only=False, event=False,
  2255. full_event=False, emboss=True, index=-1)
  2256. row = col.row(align=True)
  2257. row.prop(self, "bool_smooth")
  2258. # frame settings
  2259. if self.fill_mode == 'FRAME':
  2260. col.separator()
  2261. col.label(text="Frame Settings:")
  2262. row = col.row(align=True)
  2263. row.prop(self, "frame_mode", expand=True)
  2264. col.prop(self, "frame_thickness", text='Thickness', icon='NONE')
  2265. col.separator()
  2266. row = col.row(align=True)
  2267. row.prop(self, "fill_frame", icon='NONE')
  2268. show_frame_mat = self.bool_multi_components or self.bool_material_id
  2269. if self.fill_frame and show_frame_mat:
  2270. row.prop(self, "fill_frame_mat", icon='NONE')
  2271. row = col.row(align=True)
  2272. row.prop(self, "frame_boundary", text='Boundary', icon='NONE')
  2273. if self.frame_boundary and show_frame_mat:
  2274. row.prop(self, "frame_boundary_mat", icon='NONE')
  2275. if self.rotation_mode == 'UV':
  2276. uv_error = False
  2277. if ob0.type != 'MESH':
  2278. row = col.row(align=True)
  2279. row.label(
  2280. text="UV rotation supported only for Mesh objects",
  2281. icon='ERROR')
  2282. uv_error = True
  2283. else:
  2284. if len(ob0.data.uv_layers) == 0:
  2285. row = col.row(align=True)
  2286. check_name = self.generator
  2287. row.label(text="'" + check_name +
  2288. "' doesn't have UV Maps", icon='ERROR')
  2289. uv_error = True
  2290. if uv_error:
  2291. row = col.row(align=True)
  2292. row.label(text="Default rotation will be used instead",
  2293. icon='INFO')
  2294. # Component Z
  2295. col.separator()
  2296. col.label(text="Thickness:")
  2297. row = col.row(align=True)
  2298. row.prop(
  2299. self, "scale_mode", text="Scale Mode", icon='NONE', expand=True,
  2300. slider=False, toggle=False, icon_only=False, event=False,
  2301. full_event=False, emboss=True, index=-1)
  2302. col.prop(
  2303. self, "zscale", text="Scale", icon='NONE', expand=False,
  2304. slider=True, toggle=False, icon_only=False, event=False,
  2305. full_event=False, emboss=True, index=-1)
  2306. if self.mode == 'BOUNDS':
  2307. col.prop(
  2308. self, "offset", text="Offset", icon='NONE', expand=False,
  2309. slider=True, toggle=False, icon_only=False, event=False,
  2310. full_event=False, emboss=True, index=-1)
  2311. # Component XY
  2312. col.separator()
  2313. row = col.row(align=True)
  2314. row.label(text="Component Coordinates:")
  2315. row = col.row(align=True)
  2316. row.prop(
  2317. self, "mode", text="Component XY", icon='NONE', expand=True,
  2318. slider=False, toggle=False, icon_only=False, event=False,
  2319. full_event=False, emboss=True, index=-1)
  2320. if self.mode != 'BOUNDS':
  2321. col.separator()
  2322. row = col.row(align=True)
  2323. row.label(text="X:")
  2324. row.prop(
  2325. self, "bounds_x", text="Bounds X", icon='NONE', expand=True,
  2326. slider=False, toggle=False, icon_only=False, event=False,
  2327. full_event=False, emboss=True, index=-1)
  2328. row = col.row(align=True)
  2329. row.label(text="Y:")
  2330. row.prop(
  2331. self, "bounds_y", text="Bounds X", icon='NONE', expand=True,
  2332. slider=False, toggle=False, icon_only=False, event=False,
  2333. full_event=False, emboss=True, index=-1)
  2334. # merge settings
  2335. col = layout.column(align=True)
  2336. row = col.row(align=True)
  2337. row.prop(self, "merge")
  2338. if self.merge:
  2339. row.prop(self, "merge_thres")
  2340. col.separator()
  2341. row = col.row(align=True)
  2342. col2 = row.column(align=True)
  2343. col2.label(text='Close Mesh:')
  2344. col2 = row.column(align=True)
  2345. col2.prop(self, "close_mesh",text='')
  2346. if self.close_mesh != 'NONE':
  2347. row = col.row(align=True)
  2348. row.prop(self, "open_edges_crease", text="Crease")
  2349. row.prop(self, "cap_material_index")
  2350. if self.close_mesh == 'BRIDGE':
  2351. row = col.row(align=True)
  2352. row.prop(self, "bridge_cuts")
  2353. row.prop(self, "bridge_smoothness")
  2354. row = col.row(align=True)
  2355. row.prop(self, "bool_dissolve_seams")
  2356. # Advanced Settings
  2357. col = layout.column(align=True)
  2358. col.separator()
  2359. col.separator()
  2360. row = col.row(align=True)
  2361. row.prop(self, "bool_advanced", icon='SETTINGS')
  2362. if self.bool_advanced:
  2363. # rotation
  2364. layout.use_property_split = True
  2365. layout.use_property_decorate = False # No animation.
  2366. col = layout.column(align=True)
  2367. col.prop(self, "rotation_mode", text='Rotation', icon='NONE', expand=False,
  2368. slider=True, toggle=False, icon_only=False, event=False,
  2369. full_event=False, emboss=True, index=-1)
  2370. if self.rotation_mode == 'WEIGHT':
  2371. col.prop(self, "rotation_direction", expand=False,
  2372. slider=True, toggle=False, icon_only=False, event=False,
  2373. full_event=False, emboss=True, index=-1)
  2374. if self.rotation_mode == 'RANDOM':
  2375. col.prop(self, "random_seed")
  2376. else:
  2377. col.prop(self, "rotation_shift")
  2378. if self.rotation_mode == 'UV':
  2379. uv_error = False
  2380. if self.generator.type != 'MESH':
  2381. row = col.row(align=True)
  2382. row.label(
  2383. text="UV rotation supported only for Mesh objects",
  2384. icon='ERROR')
  2385. uv_error = True
  2386. else:
  2387. if len(self.generator.data.uv_layers) == 0:
  2388. row = col.row(align=True)
  2389. row.label(text="'" + props.generator.name +
  2390. " doesn't have UV Maps", icon='ERROR')
  2391. uv_error = True
  2392. if uv_error:
  2393. row = col.row(align=True)
  2394. row.label(text="Default rotation will be used instead",
  2395. icon='INFO')
  2396. layout.use_property_split = False
  2397. # Direction
  2398. col = layout.column(align=True)
  2399. row = col.row(align=True)
  2400. row.label(text="Direction:")
  2401. row = col.row(align=True)
  2402. row.prop(
  2403. self, "normals_mode", text="Direction", icon='NONE', expand=True,
  2404. slider=False, toggle=False, icon_only=False, event=False,
  2405. full_event=False, emboss=True, index=-1)
  2406. #row.enabled = self.fill_mode != 'PATCH'
  2407. allow_multi = False
  2408. allow_shapekeys = not self.com_modifiers
  2409. if self.com_modifiers: self.bool_shapekeys = False
  2410. for m in ob0.data.materials:
  2411. try:
  2412. o = bpy.data.objects[m.name]
  2413. allow_multi = True
  2414. try:
  2415. if o.data.shape_keys is None: continue
  2416. elif len(o.data.shape_keys.key_blocks) < 2: continue
  2417. else: allow_shapekeys = not self.com_modifiers
  2418. except: pass
  2419. except: pass
  2420. # DATA #
  2421. col = layout.column(align=True)
  2422. col.label(text="Weight and Morphing:")
  2423. # vertex group + shape keys
  2424. row = col.row(align=True)
  2425. col2 = row.column(align=True)
  2426. col2.prop(self, "bool_vertex_group", icon='GROUP_VERTEX')
  2427. try:
  2428. if len(ob0.vertex_groups) == 0:
  2429. col2.enabled = False
  2430. except:
  2431. col2.enabled = False
  2432. row.separator()
  2433. col2 = row.column(align=True)
  2434. row2 = col2.row(align=True)
  2435. row2.prop(self, "bool_shapekeys", text="Use Shape Keys", icon='SHAPEKEY_DATA')
  2436. row2.enabled = allow_shapekeys
  2437. # LIMITED TESSELLATION
  2438. col = layout.column(align=True)
  2439. col.label(text="Limited Tessellation:")
  2440. row = col.row(align=True)
  2441. col2 = row.column(align=True)
  2442. col2.prop(self, "bool_multi_components", icon='MOD_TINT')
  2443. if not allow_multi:
  2444. col2.enabled = False
  2445. self.bool_multi_components = False
  2446. col.separator()
  2447. row = col.row(align=True)
  2448. col2 = row.column(align=True)
  2449. col2.prop(self, "bool_selection", text="On selected Faces", icon='RESTRICT_SELECT_OFF')
  2450. row.separator()
  2451. if ob0.type != 'MESH':
  2452. col2.enabled = False
  2453. col2 = row.column(align=True)
  2454. col2.prop(self, "bool_material_id", icon='MATERIAL_DATA', text="Material ID")
  2455. if self.bool_material_id and not self.bool_multi_components:
  2456. #col2 = row.column(align=True)
  2457. col2.prop(self, "material_id")
  2458. col2.enabled = not self.bool_multi_components
  2459. col.separator()
  2460. row = col.row(align=True)
  2461. row.label(text='Reiterate Tessellation:', icon='FILE_REFRESH')
  2462. row.prop(self, 'iterations', text='Repeat', icon='SETTINGS')
  2463. if self.iterations > 1 and self.fill_mode == 'PATCH':
  2464. col.separator()
  2465. row = col.row(align=True)
  2466. row.prop(self, 'patch_subs')
  2467. col.separator()
  2468. row = col.row(align=True)
  2469. row.label(text='Combine Iterations:')
  2470. row = col.row(align=True)
  2471. row.prop(
  2472. self, "combine_mode", icon='NONE', expand=True,
  2473. slider=False, toggle=False, icon_only=False, event=False,
  2474. full_event=False, emboss=True, index=-1)
  2475. def execute(self, context):
  2476. allowed_obj = ('MESH', 'CURVE', 'META', 'SURFACE', 'FONT')
  2477. try:
  2478. ob0 = bpy.data.objects[self.generator]
  2479. ob1 = bpy.data.objects[self.component]
  2480. except:
  2481. return {'CANCELLED'}
  2482. self.object_name = "Tessellation"
  2483. # Check if existing object with same name
  2484. names = [o.name for o in bpy.data.objects]
  2485. if self.object_name in names:
  2486. count_name = 1
  2487. while True:
  2488. test_name = self.object_name + '.{:03d}'.format(count_name)
  2489. if not (test_name in names):
  2490. self.object_name = test_name
  2491. break
  2492. count_name += 1
  2493. if ob1.type not in allowed_obj:
  2494. message = "Component must be Mesh, Curve, Surface, Text or Meta object!"
  2495. self.report({'ERROR'}, message)
  2496. self.component = None
  2497. if ob0.type not in allowed_obj:
  2498. message = "Generator must be Mesh, Curve, Surface, Text or Meta object!"
  2499. self.report({'ERROR'}, message)
  2500. self.generator = ""
  2501. if True:#self.component not in ("",None) and self.generator not in ("",None):
  2502. if bpy.ops.object.select_all.poll():
  2503. bpy.ops.object.select_all(action='TOGGLE')
  2504. bpy.ops.object.mode_set(mode='OBJECT')
  2505. #data0 = ob0.to_mesh(False)
  2506. #data0 = ob0.data.copy()
  2507. bool_update = False
  2508. if bpy.context.object == ob0:
  2509. auto_layer_collection()
  2510. #new_ob = bpy.data.objects.new(self.object_name, data0)
  2511. new_ob = convert_object_to_mesh(ob0,False,False)
  2512. new_ob.data.name = self.object_name
  2513. #bpy.context.collection.objects.link(new_ob)
  2514. #bpy.context.view_layer.objects.active = new_ob
  2515. new_ob.name = self.object_name
  2516. #new_ob.select_set(True)
  2517. else:
  2518. new_ob = bpy.context.object
  2519. bool_update = True
  2520. new_ob = store_parameters(self, new_ob)
  2521. try: bpy.ops.object.tissue_update_tessellate()
  2522. except RuntimeError as e:
  2523. bpy.data.objects.remove(new_ob)
  2524. self.report({'ERROR'}, str(e))
  2525. return {'CANCELLED'}
  2526. if not bool_update:
  2527. self.object_name = new_ob.name
  2528. #self.working_on = self.object_name
  2529. new_ob.location = ob0.location
  2530. new_ob.matrix_world = ob0.matrix_world
  2531. return {'FINISHED'}
  2532. def invoke(self, context, event):
  2533. return context.window_manager.invoke_props_dialog(self)
  2534. def update_dependencies(ob, objects):
  2535. ob0 = ob.tissue_tessellate.generator
  2536. ob1 = ob.tissue_tessellate.component
  2537. deps = [ob0, ob1]
  2538. for o in deps:
  2539. if o.tissue_tessellate.bool_lock: continue
  2540. o0 = o.tissue_tessellate.generator
  2541. o1 = o.tissue_tessellate.component
  2542. deps_deps = [o0, o1]
  2543. try:
  2544. o0.name
  2545. o1.name
  2546. if o0 not in objects and o1 not in objects:
  2547. objects.append(o)
  2548. objects = update_dependencies(o, objects)
  2549. except:
  2550. continue
  2551. return objects
  2552. class tissue_refresh_tessellate(Operator):
  2553. bl_idname = "object.tissue_refresh_tessellate"
  2554. bl_label = "Refresh"
  2555. bl_description = ("Fast update the tessellated mesh according to base and "
  2556. "component changes")
  2557. bl_options = {'REGISTER', 'UNDO'}
  2558. go = False
  2559. @classmethod
  2560. def poll(cls, context):
  2561. try:
  2562. return context.object.tissue_tessellate.generator != None and \
  2563. context.object.tissue_tessellate.component != None
  2564. except:
  2565. return False
  2566. @staticmethod
  2567. def check_gen_comp(checking):
  2568. # note pass the stored name key in here to check it out
  2569. return checking in bpy.data.objects.keys()
  2570. def execute(self, context):
  2571. ob = bpy.context.object
  2572. ob0 = ob.tissue_tessellate.generator
  2573. ob1 = ob.tissue_tessellate.component
  2574. try:
  2575. ob0.name
  2576. ob1.name
  2577. except:
  2578. self.report({'ERROR'},
  2579. "Active object must be Tessellate before Update")
  2580. return {'CANCELLED'}
  2581. if ob.tissue_tessellate.bool_dependencies:
  2582. update_objects = list(reversed(update_dependencies(ob, [ob])))
  2583. else:
  2584. update_objects = [ob]
  2585. for o in update_objects:
  2586. override = {'object': o}
  2587. bpy.ops.object.tissue_update_tessellate(override)
  2588. return {'FINISHED'}
  2589. class tissue_update_tessellate(Operator):
  2590. bl_idname = "object.tissue_update_tessellate"
  2591. bl_label = "Refresh"
  2592. bl_description = ("Fast update the tessellated mesh according to base and "
  2593. "component changes")
  2594. bl_options = {'REGISTER', 'UNDO'}
  2595. go = False
  2596. @classmethod
  2597. def poll(cls, context):
  2598. #try:
  2599. try: #context.object == None: return False
  2600. return context.object.tissue_tessellate.generator != None and \
  2601. context.object.tissue_tessellate.component != None
  2602. except:
  2603. return False
  2604. @staticmethod
  2605. def check_gen_comp(checking):
  2606. # note pass the stored name key in here to check it out
  2607. return checking in bpy.data.objects.keys()
  2608. def execute(self, context):
  2609. start_time = time.time()
  2610. ob = context.object
  2611. if not self.go:
  2612. generator = ob.tissue_tessellate.generator
  2613. component = ob.tissue_tessellate.component
  2614. zscale = ob.tissue_tessellate.zscale
  2615. scale_mode = ob.tissue_tessellate.scale_mode
  2616. rotation_mode = ob.tissue_tessellate.rotation_mode
  2617. rotation_shift = ob.tissue_tessellate.rotation_shift
  2618. rotation_direction = ob.tissue_tessellate.rotation_direction
  2619. offset = ob.tissue_tessellate.offset
  2620. merge = ob.tissue_tessellate.merge
  2621. merge_thres = ob.tissue_tessellate.merge_thres
  2622. gen_modifiers = ob.tissue_tessellate.gen_modifiers
  2623. com_modifiers = ob.tissue_tessellate.com_modifiers
  2624. bool_random = ob.tissue_tessellate.bool_random
  2625. random_seed = ob.tissue_tessellate.random_seed
  2626. fill_mode = ob.tissue_tessellate.fill_mode
  2627. bool_vertex_group = ob.tissue_tessellate.bool_vertex_group
  2628. bool_selection = ob.tissue_tessellate.bool_selection
  2629. bool_shapekeys = ob.tissue_tessellate.bool_shapekeys
  2630. mode = ob.tissue_tessellate.mode
  2631. bool_smooth = ob.tissue_tessellate.bool_smooth
  2632. bool_materials = ob.tissue_tessellate.bool_materials
  2633. bool_dissolve_seams = ob.tissue_tessellate.bool_dissolve_seams
  2634. bool_material_id = ob.tissue_tessellate.bool_material_id
  2635. material_id = ob.tissue_tessellate.material_id
  2636. iterations = ob.tissue_tessellate.iterations
  2637. bool_combine = ob.tissue_tessellate.bool_combine
  2638. normals_mode = ob.tissue_tessellate.normals_mode
  2639. bool_advanced = ob.tissue_tessellate.bool_advanced
  2640. bool_multi_components = ob.tissue_tessellate.bool_multi_components
  2641. combine_mode = ob.tissue_tessellate.combine_mode
  2642. bounds_x = ob.tissue_tessellate.bounds_x
  2643. bounds_y = ob.tissue_tessellate.bounds_y
  2644. cap_faces = ob.tissue_tessellate.cap_faces
  2645. close_mesh = ob.tissue_tessellate.close_mesh
  2646. open_edges_crease = ob.tissue_tessellate.open_edges_crease
  2647. bridge_smoothness = ob.tissue_tessellate.bridge_smoothness
  2648. frame_thickness = ob.tissue_tessellate.frame_thickness
  2649. frame_mode = ob.tissue_tessellate.frame_mode
  2650. frame_boundary = ob.tissue_tessellate.frame_boundary
  2651. fill_frame = ob.tissue_tessellate.fill_frame
  2652. frame_boundary_mat = ob.tissue_tessellate.frame_boundary_mat
  2653. fill_frame_mat = ob.tissue_tessellate.fill_frame_mat
  2654. bridge_cuts = ob.tissue_tessellate.bridge_cuts
  2655. cap_material_index = ob.tissue_tessellate.cap_material_index
  2656. patch_subs = ob.tissue_tessellate.patch_subs
  2657. try:
  2658. generator.name
  2659. component.name
  2660. except:
  2661. self.report({'ERROR'},
  2662. "Active object must be Tessellate before Update")
  2663. return {'CANCELLED'}
  2664. # Solve Local View issues
  2665. local_spaces = []
  2666. local_ob0 = []
  2667. local_ob1 = []
  2668. for area in context.screen.areas:
  2669. for space in area.spaces:
  2670. try:
  2671. if ob.local_view_get(space):
  2672. local_spaces.append(space)
  2673. local_ob0 = ob0.local_view_get(space)
  2674. ob0.local_view_set(space, True)
  2675. local_ob1 = ob1.local_view_get(space)
  2676. ob1.local_view_set(space, True)
  2677. except:
  2678. pass
  2679. starting_mode = context.object.mode
  2680. #if starting_mode == 'PAINT_WEIGHT': starting_mode = 'WEIGHT_PAINT'
  2681. bpy.ops.object.mode_set(mode='OBJECT')
  2682. ob0 = generator
  2683. ob1 = component
  2684. ##### auto_layer_collection()
  2685. ob0_hide = ob0.hide_get()
  2686. ob0_hidev = ob0.hide_viewport
  2687. ob0_hider = ob0.hide_render
  2688. ob1_hide = ob1.hide_get()
  2689. ob1_hidev = ob1.hide_viewport
  2690. ob1_hider = ob1.hide_render
  2691. ob0.hide_set(False)
  2692. ob0.hide_viewport = False
  2693. ob0.hide_render = False
  2694. ob1.hide_set(False)
  2695. ob1.hide_viewport = False
  2696. ob1.hide_render = False
  2697. if ob0.type == 'META':
  2698. base_ob = convert_object_to_mesh(ob0, False, True)
  2699. else:
  2700. base_ob = ob0.copy()
  2701. base_ob.data = ob0.data#
  2702. context.collection.objects.link(base_ob)
  2703. base_ob.name = '_tissue_tmp_base'
  2704. # In Blender 2.80 cache of copied objects is lost, must be re-baked
  2705. bool_update_cloth = False
  2706. for m in base_ob.modifiers:
  2707. if m.type == 'CLOTH':
  2708. m.point_cache.frame_end = context.scene.frame_current
  2709. bool_update_cloth = True
  2710. if bool_update_cloth:
  2711. bpy.ops.ptcache.free_bake_all()
  2712. bpy.ops.ptcache.bake_all()
  2713. base_ob.modifiers.update()
  2714. #new_ob.location = ob.location
  2715. #new_ob.matrix_world = ob.matrix_world
  2716. #bpy.ops.object.select_all(action='DESELECT')
  2717. if bool_selection:
  2718. faces = base_ob.data.polygons
  2719. selections = [False]*len(faces)
  2720. faces.foreach_get('select',selections)
  2721. selections = np.array(selections)
  2722. if not selections.any():
  2723. message = "There are no faces selected."
  2724. context.view_layer.objects.active = ob
  2725. ob.select_set(True)
  2726. bpy.ops.object.mode_set(mode=starting_mode)
  2727. bpy.data.objects.remove(base_ob)
  2728. self.report({'ERROR'}, message)
  2729. return {'CANCELLED'}
  2730. iter_objects = [base_ob]
  2731. ob_location = ob.location
  2732. ob_matrix_world = ob.matrix_world
  2733. #base_ob = new_ob#.copy()
  2734. for iter in range(iterations):
  2735. if iter > 0 and len(iter_objects) == 0: break
  2736. if iter > 0 and normals_mode == 'CUSTOM': normals_mode = 'VERTS'
  2737. same_iteration = []
  2738. matched_materials = []
  2739. # iterate base object materials (needed for multi-components)
  2740. if bool_multi_components: mat_iter = len(base_ob.material_slots)
  2741. else: mat_iter = 1
  2742. for m_id in range(mat_iter):
  2743. if bool_multi_components:
  2744. # check if material and components match
  2745. try:
  2746. mat = base_ob.material_slots[m_id].material
  2747. ob1 = bpy.data.objects[mat.name]
  2748. if ob1.type not in ('MESH', 'CURVE','SURFACE','FONT', 'META'):
  2749. continue
  2750. material_id = m_id
  2751. matched_materials.append(m_id)
  2752. bool_material_id = True
  2753. except:
  2754. continue
  2755. if com_modifiers or ob1.type != 'MESH':
  2756. data1 = simple_to_mesh(ob1)
  2757. else:
  2758. data1 = ob1.data.copy()
  2759. n_edges1 = len(data1.edges)
  2760. bpy.data.meshes.remove(data1)
  2761. if iter != 0: gen_modifiers = True
  2762. if fill_mode == 'PATCH':
  2763. # patch subdivisions for additional iterations
  2764. if iter > 0:
  2765. base_ob.modifiers.new('Tissue_Subsurf', type='SUBSURF')
  2766. base_ob.modifiers['Tissue_Subsurf'].levels = patch_subs
  2767. temp_mod = base_ob.modifiers['Tissue_Subsurf']
  2768. # patch tessellation
  2769. new_ob = tessellate_patch(
  2770. base_ob, ob1, offset, zscale, com_modifiers, mode, scale_mode,
  2771. rotation_mode, rotation_shift, random_seed, bool_vertex_group,
  2772. bool_selection, bool_shapekeys, bool_material_id, material_id,
  2773. normals_mode, bounds_x, bounds_y
  2774. )
  2775. if iter > 0:
  2776. base_ob.modifiers.remove(temp_mod)
  2777. else:
  2778. ### FRAME and FAN ###
  2779. if fill_mode in ('FRAME','FAN'):
  2780. if fill_mode == 'FRAME': convert_function = convert_to_frame
  2781. else: convert_function = convert_to_fan
  2782. if normals_mode == 'CUSTOM' and base_ob.data.shape_keys != None:
  2783. ## base key
  2784. sk_values = [sk.value for sk in base_ob.data.shape_keys.key_blocks]
  2785. for sk in ob0.data.shape_keys.key_blocks: sk.value = 0
  2786. _base_ob = convert_function(base_ob, ob.tissue_tessellate, gen_modifiers)
  2787. for i, sk in enumerate(ob0.data.shape_keys.key_blocks):
  2788. sk.value = sk_values[i]
  2789. ## key 1
  2790. # hide modifiers
  2791. if not gen_modifiers and len(base_ob.modifiers) > 0:
  2792. mod_visibility = [m.show_viewport for m in base_ob.modifiers]
  2793. for m in base_ob.modifiers: m.show_viewport = False
  2794. base_ob.modifiers.update()
  2795. base_ob_sk = convert_function(ob0, ob.tissue_tessellate, True)
  2796. ## combine shapekeys
  2797. _base_ob.shape_key_add(name='Basis', from_mix=False)
  2798. _base_ob.shape_key_add(name='Key1', from_mix=False)
  2799. sk_block = _base_ob.data.shape_keys.key_blocks[1]
  2800. sk_block.value = 1
  2801. for vert, sk in zip(base_ob_sk.data.vertices, sk_block.data):
  2802. sk.co = vert.co
  2803. bpy.data.objects.remove(base_ob_sk)
  2804. # set original modifiers
  2805. if not gen_modifiers and len(base_ob.modifiers) > 0:
  2806. for i,m in enumerate(base_ob.modifiers):
  2807. m.show_viewport = mod_visibility[i]
  2808. base_ob.modifiers.update()
  2809. else:
  2810. _base_ob = convert_function(base_ob, ob.tissue_tessellate, gen_modifiers)
  2811. bpy.data.objects.remove(base_ob)
  2812. base_ob = _base_ob
  2813. # quad tessellation
  2814. new_ob = tessellate_original(
  2815. base_ob, ob1, offset, zscale, gen_modifiers,
  2816. com_modifiers, mode, scale_mode, rotation_mode,
  2817. rotation_shift, rotation_direction,
  2818. random_seed, fill_mode, bool_vertex_group,
  2819. bool_selection, bool_shapekeys, bool_material_id,
  2820. material_id, normals_mode, bounds_x, bounds_y
  2821. )
  2822. # if empty or error, continue
  2823. if type(new_ob) is not bpy.types.Object:
  2824. continue
  2825. # prepare base object
  2826. if iter == 0 and gen_modifiers:
  2827. temp_base_ob = convert_object_to_mesh(base_ob, True, True)
  2828. bpy.data.objects.remove(base_ob)
  2829. base_ob = temp_base_ob
  2830. iter_objects = [base_ob]
  2831. # rename, make active and change transformations
  2832. new_ob.name = '_tissue_tmp_{}_{}'.format(iter,m_id)
  2833. new_ob.select_set(True)
  2834. context.view_layer.objects.active = new_ob
  2835. new_ob.location = ob_location
  2836. new_ob.matrix_world = ob_matrix_world
  2837. n_components = int(len(new_ob.data.edges) / n_edges1)
  2838. # SELECTION
  2839. if bool_selection:
  2840. try:
  2841. # create selection list
  2842. polygon_selection = [p.select for p in ob1.data.polygons] * int(
  2843. len(new_ob.data.polygons) / len(ob1.data.polygons))
  2844. new_ob.data.polygons.foreach_set("select", polygon_selection)
  2845. except:
  2846. pass
  2847. if bool_multi_components: same_iteration.append(new_ob)
  2848. base_ob.location = ob_location
  2849. base_ob.matrix_world = ob_matrix_world
  2850. # join together multiple components iterations
  2851. if bool_multi_components:
  2852. if len(same_iteration) > 0:
  2853. context.view_layer.update()
  2854. for o in context.view_layer.objects:
  2855. o.select_set(o in same_iteration)
  2856. bpy.ops.object.join()
  2857. new_ob = context.view_layer.objects.active
  2858. new_ob.select_set(True)
  2859. #new_ob.data.update()
  2860. if type(new_ob) in (int,str):
  2861. if iter == 0:
  2862. try:
  2863. bpy.data.objects.remove(iter_objects[0])
  2864. iter_objects = []
  2865. except: continue
  2866. continue
  2867. # Clean last iteration, needed for combine object
  2868. if (bool_selection or bool_material_id) and combine_mode == 'UNUSED':
  2869. # remove faces from last mesh
  2870. bm = bmesh.new()
  2871. last_mesh = iter_objects[-1].data.copy()
  2872. bm.from_mesh(last_mesh)
  2873. bm.faces.ensure_lookup_table()
  2874. if bool_multi_components:
  2875. remove_materials = matched_materials
  2876. elif bool_material_id:
  2877. remove_materials = [material_id]
  2878. else: remove_materials = []
  2879. if bool_selection:
  2880. if bool_multi_components or bool_material_id:
  2881. remove_faces = [f for f in bm.faces if f.material_index in remove_materials and f.select]
  2882. else:
  2883. remove_faces = [f for f in bm.faces if f.select]
  2884. else:
  2885. remove_faces = [f for f in bm.faces if f.material_index in remove_materials]
  2886. bmesh.ops.delete(bm, geom=remove_faces, context='FACES')
  2887. bm.to_mesh(last_mesh)
  2888. last_mesh.update()
  2889. last_mesh.name = '_tissue_tmp_previous_unused'
  2890. # delete previous iteration if empty or update it
  2891. if len(last_mesh.vertices) > 0:
  2892. iter_objects[-1].data = last_mesh.copy()
  2893. iter_objects[-1].data.update()
  2894. else:
  2895. bpy.data.objects.remove(iter_objects[-1])
  2896. iter_objects = iter_objects[:-1]
  2897. # set new base object for next iteration
  2898. base_ob = convert_object_to_mesh(new_ob,True,True)
  2899. if iter < iterations-1: new_ob.data = base_ob.data
  2900. # store new iteration and set transformations
  2901. iter_objects.append(new_ob)
  2902. #try:
  2903. # bpy.data.objects.remove(bpy.data.objects['_tissue_tmp_base'])
  2904. #except:
  2905. # pass
  2906. base_ob.name = '_tissue_tmp_base'
  2907. elif combine_mode == 'ALL':
  2908. base_ob = new_ob.copy()
  2909. iter_objects.append(new_ob)
  2910. else:
  2911. if base_ob != new_ob:
  2912. bpy.data.objects.remove(base_ob)
  2913. base_ob = new_ob
  2914. iter_objects = [new_ob]
  2915. # Combine
  2916. if combine_mode != 'LAST' and len(iter_objects)>0:
  2917. if base_ob not in iter_objects and type(base_ob) == bpy.types.Object:
  2918. bpy.data.objects.remove(base_ob)
  2919. for o in context.view_layer.objects:
  2920. o.select_set(o in iter_objects)
  2921. bpy.ops.object.join()
  2922. new_ob.data.update()
  2923. iter_objects = [new_ob]
  2924. if merge:
  2925. bpy.ops.object.mode_set(mode='EDIT')
  2926. bpy.ops.mesh.select_mode(
  2927. use_extend=False, use_expand=False, type='VERT')
  2928. bpy.ops.mesh.select_non_manifold(
  2929. extend=False, use_wire=True, use_boundary=True,
  2930. use_multi_face=False, use_non_contiguous=False, use_verts=False)
  2931. bpy.ops.mesh.remove_doubles(
  2932. threshold=merge_thres, use_unselected=False)
  2933. if bool_dissolve_seams:
  2934. bpy.ops.mesh.select_mode(type='EDGE')
  2935. bpy.ops.mesh.select_all(action='DESELECT')
  2936. bpy.ops.object.mode_set(mode='OBJECT')
  2937. for e in new_ob.data.edges:
  2938. e.select = e.use_seam
  2939. bpy.ops.object.mode_set(mode='EDIT')
  2940. bpy.ops.mesh.dissolve_edges()
  2941. bpy.ops.object.mode_set(mode='OBJECT')
  2942. if close_mesh != 'NONE':
  2943. bpy.ops.object.mode_set(mode='EDIT')
  2944. bpy.ops.mesh.select_mode(
  2945. use_extend=False, use_expand=False, type='EDGE')
  2946. bpy.ops.mesh.select_non_manifold(
  2947. extend=False, use_wire=False, use_boundary=True,
  2948. use_multi_face=False, use_non_contiguous=False, use_verts=False)
  2949. if open_edges_crease != 0:
  2950. bpy.ops.transform.edge_crease(value=open_edges_crease)
  2951. if close_mesh == 'CAP':
  2952. bpy.ops.mesh.edge_face_add()
  2953. if close_mesh == 'BRIDGE':
  2954. try:
  2955. bpy.ops.mesh.bridge_edge_loops(
  2956. type='PAIRS',
  2957. number_cuts=bridge_cuts,
  2958. interpolation='SURFACE',
  2959. smoothness=bridge_smoothness)
  2960. except: pass
  2961. bpy.ops.object.mode_set(mode='OBJECT')
  2962. for f in new_ob.data.polygons:
  2963. if f.select: f.material_index = cap_material_index
  2964. base_ob = context.view_layer.objects.active
  2965. # Combine iterations
  2966. if combine_mode != 'LAST' and len(iter_objects)>0:
  2967. #if base_ob not in iter_objects and type(base_ob) == bpy.types.Object:
  2968. # bpy.data.objects.remove(base_ob)
  2969. for o in context.view_layer.objects:
  2970. o.select_set(o in iter_objects)
  2971. bpy.ops.object.join()
  2972. new_ob = context.view_layer.objects.active
  2973. elif combine_mode == 'LAST' and type(new_ob) != bpy.types.Object:
  2974. # if last iteration gives error, then use the last correct iteration
  2975. try:
  2976. if type(iter_objects[-1]) == bpy.types.Object:
  2977. new_ob = iter_objects[-1]
  2978. except: pass
  2979. if new_ob == 0:
  2980. #bpy.data.objects.remove(base_ob.data)
  2981. try: bpy.data.objects.remove(base_ob)
  2982. except: pass
  2983. message = "The generated object is an empty geometry!"
  2984. context.view_layer.objects.active = ob
  2985. ob.select_set(True)
  2986. bpy.ops.object.mode_set(mode=starting_mode)
  2987. self.report({'ERROR'}, message)
  2988. return {'CANCELLED'}
  2989. errors = {}
  2990. errors["modifiers_error"] = "Modifiers that change the topology of the mesh \n" \
  2991. "after the last Subsurf (or Multires) are not allowed."
  2992. errors["topology_error"] = "Make sure that the topology of the mesh before \n" \
  2993. "the last Subsurf (or Multires) is quads only."
  2994. errors["wires_error"] = "Please remove all wire edges in the base object."
  2995. errors["verts_error"] = "Please remove all floating vertices in the base object"
  2996. if new_ob in errors:
  2997. for o in iter_objects:
  2998. try: bpy.data.objects.remove(o)
  2999. except: pass
  3000. try: bpy.data.meshes.remove(data1)
  3001. except: pass
  3002. context.view_layer.objects.active = ob
  3003. ob.select_set(True)
  3004. message = errors[new_ob]
  3005. ob.tissue_tessellate.error_message = message
  3006. bpy.ops.object.mode_set(mode=starting_mode)
  3007. self.report({'ERROR'}, message)
  3008. return {'CANCELLED'}
  3009. #new_ob.location = ob_location
  3010. #new_ob.matrix_world = ob_matrix_world
  3011. # update data and preserve name
  3012. if ob.type != 'MESH':
  3013. loc, matr = ob.location, ob.matrix_world
  3014. ob = convert_object_to_mesh(ob,False,True)
  3015. ob.location, ob.matrix_world = loc, matr
  3016. data_name = ob.data.name
  3017. old_data = ob.data
  3018. #ob.data = bpy.data.meshes.new_from_object(new_ob)#
  3019. ob.data = new_ob.data.copy()
  3020. ob.data.name = data_name
  3021. bpy.data.meshes.remove(old_data)
  3022. # copy vertex group
  3023. if bool_vertex_group:
  3024. for vg in new_ob.vertex_groups:
  3025. if not vg.name in ob.vertex_groups.keys():
  3026. ob.vertex_groups.new(name=vg.name)
  3027. new_vg = ob.vertex_groups[vg.name]
  3028. for i in range(len(ob.data.vertices)):
  3029. try:
  3030. weight = vg.weight(i)
  3031. except:
  3032. weight = 0
  3033. new_vg.add([i], weight, 'REPLACE')
  3034. selected_objects = [o for o in context.selected_objects]
  3035. for o in selected_objects: o.select_set(False)
  3036. ob.select_set(True)
  3037. context.view_layer.objects.active = ob
  3038. if merge:
  3039. try:
  3040. bpy.ops.object.mode_set(mode='EDIT')
  3041. #bpy.ops.mesh.select_mode(
  3042. # use_extend=False, use_expand=False, type='VERT')
  3043. bpy.ops.mesh.select_mode(type='VERT')
  3044. bpy.ops.mesh.select_non_manifold(
  3045. extend=False, use_wire=True, use_boundary=True,
  3046. use_multi_face=False, use_non_contiguous=False, use_verts=False)
  3047. bpy.ops.mesh.remove_doubles(
  3048. threshold=merge_thres, use_unselected=False)
  3049. bpy.ops.object.mode_set(mode='OBJECT')
  3050. if bool_dissolve_seams:
  3051. bpy.ops.object.mode_set(mode='EDIT')
  3052. bpy.ops.mesh.select_mode(type='EDGE')
  3053. bpy.ops.mesh.select_all(action='DESELECT')
  3054. bpy.ops.object.mode_set(mode='OBJECT')
  3055. for e in ob.data.edges:
  3056. e.select = e.use_seam
  3057. bpy.ops.object.mode_set(mode='EDIT')
  3058. bpy.ops.mesh.dissolve_edges()
  3059. except: pass
  3060. if close_mesh != 'NONE':
  3061. bpy.ops.object.mode_set(mode='EDIT')
  3062. bpy.ops.mesh.select_mode(
  3063. use_extend=False, use_expand=False, type='EDGE')
  3064. bpy.ops.mesh.select_non_manifold(
  3065. extend=False, use_wire=False, use_boundary=True,
  3066. use_multi_face=False, use_non_contiguous=False, use_verts=False)
  3067. if open_edges_crease != 0:
  3068. bpy.ops.transform.edge_crease(value=open_edges_crease)
  3069. if close_mesh == 'CAP':
  3070. bpy.ops.mesh.edge_face_add()
  3071. if close_mesh == 'BRIDGE':
  3072. try:
  3073. bpy.ops.mesh.bridge_edge_loops(
  3074. type='PAIRS',
  3075. number_cuts=bridge_cuts,
  3076. interpolation='SURFACE',
  3077. smoothness=bridge_smoothness)
  3078. except:
  3079. pass
  3080. bpy.ops.object.mode_set(mode='OBJECT')
  3081. for f in ob.data.polygons:
  3082. if f.select: f.material_index = cap_material_index
  3083. #else:
  3084. try:
  3085. bpy.ops.object.mode_set(mode='EDIT')
  3086. bpy.ops.object.mode_set(mode='OBJECT')
  3087. except: pass
  3088. if bool_smooth: bpy.ops.object.shade_smooth()
  3089. for mesh in bpy.data.meshes:
  3090. if not mesh.users: bpy.data.meshes.remove(mesh)
  3091. for o in selected_objects:
  3092. try: o.select_set(True)
  3093. except: pass
  3094. bpy.ops.object.mode_set(mode=starting_mode)
  3095. ob.tissue_tessellate.error_message = ""
  3096. # Restore Base visibility
  3097. ob0.hide_set(ob0_hide)
  3098. ob0.hide_viewport = ob0_hidev
  3099. ob0.hide_render = ob0_hider
  3100. # Restore Component visibility
  3101. ob1.hide_set(ob1_hide)
  3102. ob1.hide_viewport = ob1_hidev
  3103. ob1.hide_render = ob1_hider
  3104. # Restore Local visibility
  3105. for space, local0, local1 in zip(local_spaces, local_ob0, local_ob1):
  3106. ob0.local_view_set(space, local0)
  3107. ob1.local_view_set(space, local1)
  3108. bpy.data.objects.remove(new_ob)
  3109. # clean objects
  3110. for o in bpy.data.objects:
  3111. #if o.name not in context.view_layer.objects and "_tissue_tmp" in o.name:
  3112. if "_tissue_tmp" in o.name:
  3113. bpy.data.objects.remove(o)
  3114. end_time = time.time()
  3115. print('Tissue: object "{}" tessellated in {:.4f} sec'.format(ob.name, end_time-start_time))
  3116. return {'FINISHED'}
  3117. def check(self, context):
  3118. return True
  3119. class TISSUE_PT_tessellate(Panel):
  3120. bl_label = "Tissue Tools"
  3121. bl_category = "Tissue"
  3122. bl_space_type = "VIEW_3D"
  3123. bl_region_type = "UI"
  3124. #bl_options = {'DEFAULT_OPEN'}
  3125. @classmethod
  3126. def poll(cls, context):
  3127. return context.mode in {'OBJECT', 'EDIT_MESH'}
  3128. def draw(self, context):
  3129. layout = self.layout
  3130. col = layout.column(align=True)
  3131. col.label(text="Tessellate:")
  3132. col.operator("object.tissue_tessellate")
  3133. col.operator("object.dual_mesh_tessellated")
  3134. col.separator()
  3135. col.operator("object.tissue_refresh_tessellate", icon='FILE_REFRESH')
  3136. col.separator()
  3137. col.label(text="Rotate Faces:")
  3138. row = col.row(align=True)
  3139. row.operator("mesh.tissue_rotate_face_left", text='Left', icon='LOOP_BACK')
  3140. row.operator("mesh.tissue_rotate_face_right", text='Right', icon='LOOP_FORWARDS')
  3141. col.separator()
  3142. col.label(text="Other:")
  3143. col.operator("object.dual_mesh")
  3144. col.operator("object.lattice_along_surface", icon="OUTLINER_OB_LATTICE")
  3145. act = context.object
  3146. if act and act.type == 'MESH':
  3147. col.operator("object.uv_to_mesh", icon="UV")
  3148. if act.mode == 'EDIT':
  3149. col.separator()
  3150. col.label(text="Weight:")
  3151. col.operator("object.tissue_weight_distance", icon="TRACKING")
  3152. class TISSUE_PT_tessellate_object(Panel):
  3153. bl_space_type = 'PROPERTIES'
  3154. bl_region_type = 'WINDOW'
  3155. bl_context = "data"
  3156. bl_label = "Tessellate Settings"
  3157. bl_options = {'DEFAULT_CLOSED'}
  3158. @classmethod
  3159. def poll(cls, context):
  3160. try: return context.object.type == 'MESH'
  3161. except: return False
  3162. def draw(self, context):
  3163. ob = context.object
  3164. props = ob.tissue_tessellate
  3165. allowed_obj = ('MESH','CURVE','SURFACE','FONT', 'META')
  3166. try:
  3167. bool_tessellated = props.generator or props.component != None
  3168. ob0 = props.generator
  3169. ob1 = props.component
  3170. except: bool_tessellated = False
  3171. layout = self.layout
  3172. if not bool_tessellated:
  3173. layout.label(text="The selected object is not a Tessellated object",
  3174. icon='INFO')
  3175. else:
  3176. if props.error_message != "":
  3177. layout.label(text=props.error_message,
  3178. icon='ERROR')
  3179. col = layout.column(align=True)
  3180. row = col.row(align=True)
  3181. set_tessellate_handler(self,context)
  3182. set_animatable_fix_handler(self,context)
  3183. row.operator("object.tissue_refresh_tessellate", icon='FILE_REFRESH')
  3184. lock_icon = 'LOCKED' if props.bool_lock else 'UNLOCKED'
  3185. #lock_icon = 'PINNED' if props.bool_lock else 'UNPINNED'
  3186. deps_icon = 'LINKED' if props.bool_dependencies else 'UNLINKED'
  3187. row.prop(props, "bool_dependencies", text="", icon=deps_icon)
  3188. row.prop(props, "bool_lock", text="", icon=lock_icon)
  3189. col2 = row.column(align=True)
  3190. col2.prop(props, "bool_run", text="",icon='TIME')
  3191. col2.enabled = not props.bool_lock
  3192. '''
  3193. col = layout.column(align=True)
  3194. row = col.row(align=True)
  3195. row.label(text="Base :")
  3196. row.label(text="Component :")
  3197. row = col.row(align=True)
  3198. col2 = row.column(align=True)
  3199. col2.prop_search(props, "generator", context.scene, "objects")
  3200. row.separator()
  3201. col2 = row.column(align=True)
  3202. col2.prop_search(props, "component", context.scene, "objects")
  3203. row = col.row(align=True)
  3204. col2 = row.column(align=True)
  3205. col2.prop(props, "gen_modifiers", text="Use Modifiers", icon='MODIFIER')
  3206. row.separator()
  3207. try:
  3208. if not (ob0.modifiers or ob0.data.shape_keys) or props.fill_mode == 'PATCH':
  3209. col2.enabled = False
  3210. except:
  3211. col2.enabled = False
  3212. col2 = row.column(align=True)
  3213. col2.prop(props, "com_modifiers", text="Use Modifiers", icon='MODIFIER')
  3214. try:
  3215. if not (props.component.modifiers or props.component.data.shape_keys):
  3216. col2.enabled = False
  3217. except:
  3218. col2.enabled = False
  3219. '''
  3220. layout.use_property_split = True
  3221. layout.use_property_decorate = False # No animation.
  3222. col = layout.column(align=True)
  3223. row = col.row(align=True)
  3224. row.label(text='Base:')
  3225. row.prop_search(props, "generator", context.scene, "objects")
  3226. col2 = row.column(align=True)
  3227. col2.prop(props, "gen_modifiers", text='',icon='MODIFIER')
  3228. try:
  3229. if not (props.generator.modifiers or props.generator.data.shape_keys):
  3230. col2.enabled = False
  3231. except:
  3232. col2.enabled = False
  3233. col.separator()
  3234. row = col.row(align=True)
  3235. row.label(text='Component:')
  3236. row.prop_search(props, "component", context.scene, "objects")
  3237. col2 = row.column(align=True)
  3238. col2.prop(props, "com_modifiers", text='',icon='MODIFIER')
  3239. try:
  3240. if not (props.component.modifiers or props.component.data.shape_keys):
  3241. col2.enabled = False
  3242. except:
  3243. col2.enabled = False
  3244. layout.use_property_split = False
  3245. # Fill
  3246. col = layout.column(align=True)
  3247. col.label(text="Fill Mode:")
  3248. # fill
  3249. row = col.row(align=True)
  3250. row.prop(props, "fill_mode", icon='NONE', expand=True,
  3251. slider=True, toggle=False, icon_only=False, event=False,
  3252. full_event=False, emboss=True, index=-1)
  3253. #layout.use_property_split = True
  3254. col = layout.column(align=True)
  3255. col.prop(props, "bool_smooth")
  3256. class TISSUE_PT_tessellate_frame(Panel):
  3257. bl_space_type = 'PROPERTIES'
  3258. bl_region_type = 'WINDOW'
  3259. bl_context = "data"
  3260. bl_parent_id = "TISSUE_PT_tessellate_object"
  3261. bl_label = "Frame Settings"
  3262. #bl_options = {'DEFAULT_CLOSED'}
  3263. @classmethod
  3264. def poll(cls, context):
  3265. try:
  3266. bool_frame = context.object.tissue_tessellate.fill_mode == 'FRAME'
  3267. bool_tessellated = context.object.tissue_tessellate.generator != None
  3268. return context.object.type == 'MESH' and bool_frame and bool_tessellated
  3269. except:
  3270. return False
  3271. def draw(self, context):
  3272. ob = context.object
  3273. props = ob.tissue_tessellate
  3274. allowed_obj = ('MESH','CURVE','SURFACE','FONT', 'META')
  3275. try:
  3276. bool_tessellated = props.generator or props.component != None
  3277. ob0 = props.generator
  3278. ob1 = props.component
  3279. except: bool_tessellated = False
  3280. layout = self.layout
  3281. if bool_tessellated:
  3282. col = layout.column(align=True)
  3283. row = col.row(align=True)
  3284. row.prop(props, "frame_mode", expand=True)
  3285. row = col.row(align=True)
  3286. row.prop(props, "frame_thickness", icon='NONE', expand=True)
  3287. row = col.row(align=True)
  3288. row.prop(props, "fill_frame", icon='NONE')
  3289. show_frame_mat = props.bool_multi_components or props.bool_material_id
  3290. if props.fill_frame and show_frame_mat:
  3291. row.prop(props, "fill_frame_mat", icon='NONE')
  3292. row = col.row(align=True)
  3293. row.prop(props, "frame_boundary", text='Boundary', icon='NONE')
  3294. if props.frame_boundary and show_frame_mat:
  3295. row.prop(props, "frame_boundary_mat", icon='NONE')
  3296. class TISSUE_PT_tessellate_coordinates(Panel):
  3297. bl_space_type = 'PROPERTIES'
  3298. bl_region_type = 'WINDOW'
  3299. bl_context = "data"
  3300. bl_parent_id = "TISSUE_PT_tessellate_object"
  3301. bl_label = "Component Coordinates"
  3302. bl_options = {'DEFAULT_CLOSED'}
  3303. @classmethod
  3304. def poll(cls, context):
  3305. try:
  3306. bool_tessellated = context.object.tissue_tessellate.generator != None
  3307. return context.object.type == 'MESH' and bool_tessellated
  3308. except:
  3309. return False
  3310. def draw(self, context):
  3311. ob = context.object
  3312. props = ob.tissue_tessellate
  3313. allowed_obj = ('MESH','CURVE','SURFACE','FONT', 'META')
  3314. try:
  3315. bool_tessellated = props.generator or props.component != None
  3316. ob0 = props.generator
  3317. ob1 = props.component
  3318. except: bool_tessellated = False
  3319. layout = self.layout
  3320. if bool_tessellated:
  3321. col = layout.column(align=True)
  3322. # component XY
  3323. row = col.row(align=True)
  3324. row.prop(props, "mode", expand=True)
  3325. if props.mode != 'BOUNDS':
  3326. col.separator()
  3327. row = col.row(align=True)
  3328. row.label(text="X:")
  3329. row.prop(
  3330. props, "bounds_x", text="Bounds X", icon='NONE', expand=True,
  3331. slider=False, toggle=False, icon_only=False, event=False,
  3332. full_event=False, emboss=True, index=-1)
  3333. row = col.row(align=True)
  3334. row.label(text="Y:")
  3335. row.prop(
  3336. props, "bounds_y", text="Bounds X", icon='NONE', expand=True,
  3337. slider=False, toggle=False, icon_only=False, event=False,
  3338. full_event=False, emboss=True, index=-1)
  3339. class TISSUE_PT_tessellate_rotation(Panel):
  3340. bl_space_type = 'PROPERTIES'
  3341. bl_region_type = 'WINDOW'
  3342. bl_context = "data"
  3343. bl_parent_id = "TISSUE_PT_tessellate_object"
  3344. bl_label = "Rotation"
  3345. bl_options = {'DEFAULT_CLOSED'}
  3346. @classmethod
  3347. def poll(cls, context):
  3348. try:
  3349. bool_tessellated = context.object.tissue_tessellate.generator != None
  3350. return context.object.type == 'MESH' and bool_tessellated
  3351. except:
  3352. return False
  3353. def draw(self, context):
  3354. ob = context.object
  3355. props = ob.tissue_tessellate
  3356. allowed_obj = ('MESH','CURVE','SURFACE','FONT', 'META')
  3357. try:
  3358. bool_tessellated = props.generator or props.component != None
  3359. ob0 = props.generator
  3360. ob1 = props.component
  3361. except: bool_tessellated = False
  3362. layout = self.layout
  3363. if bool_tessellated:
  3364. # rotation
  3365. layout.use_property_split = True
  3366. layout.use_property_decorate = False # No animation.
  3367. col = layout.column(align=True)
  3368. col.prop(props, "rotation_mode", text='Rotation', icon='NONE', expand=False,
  3369. slider=True, toggle=False, icon_only=False, event=False,
  3370. full_event=False, emboss=True, index=-1)
  3371. if props.rotation_mode == 'WEIGHT':
  3372. col.prop(props, "rotation_direction", expand=False,
  3373. slider=True, toggle=False, icon_only=False, event=False,
  3374. full_event=False, emboss=True, index=-1)
  3375. if props.rotation_mode == 'RANDOM':
  3376. col.prop(props, "random_seed")
  3377. else:
  3378. col.prop(props, "rotation_shift")
  3379. if props.rotation_mode == 'UV':
  3380. uv_error = False
  3381. if props.generator.type != 'MESH':
  3382. row = col.row(align=True)
  3383. row.label(
  3384. text="UV rotation supported only for Mesh objects",
  3385. icon='ERROR')
  3386. uv_error = True
  3387. else:
  3388. if len(props.generator.data.uv_layers) == 0:
  3389. row = col.row(align=True)
  3390. row.label(text="'" + props.generator.name +
  3391. " doesn't have UV Maps", icon='ERROR')
  3392. uv_error = True
  3393. if uv_error:
  3394. row = col.row(align=True)
  3395. row.label(text="Default rotation will be used instead",
  3396. icon='INFO')
  3397. class TISSUE_PT_tessellate_thickness(Panel):
  3398. bl_space_type = 'PROPERTIES'
  3399. bl_region_type = 'WINDOW'
  3400. bl_context = "data"
  3401. bl_parent_id = "TISSUE_PT_tessellate_object"
  3402. bl_label = "Thickness"
  3403. #bl_options = {'DEFAULT_CLOSED'}
  3404. @classmethod
  3405. def poll(cls, context):
  3406. try:
  3407. bool_tessellated = context.object.tissue_tessellate.generator != None
  3408. return context.object.type == 'MESH' and bool_tessellated
  3409. except:
  3410. return False
  3411. def draw(self, context):
  3412. ob = context.object
  3413. props = ob.tissue_tessellate
  3414. allowed_obj = ('MESH','CURVE','SURFACE','FONT', 'META')
  3415. try:
  3416. bool_tessellated = props.generator or props.component != None
  3417. ob0 = props.generator
  3418. ob1 = props.component
  3419. except: bool_tessellated = False
  3420. layout = self.layout
  3421. #layout.use_property_split = True
  3422. if bool_tessellated:
  3423. col = layout.column(align=True)
  3424. # component Z
  3425. row = col.row(align=True)
  3426. row.prop(props, "scale_mode", expand=True)
  3427. col.prop(props, "zscale", text="Scale", icon='NONE', expand=False,
  3428. slider=True, toggle=False, icon_only=False, event=False,
  3429. full_event=False, emboss=True, index=-1)
  3430. if props.mode == 'BOUNDS':
  3431. col.prop(props, "offset", text="Offset", icon='NONE', expand=False,
  3432. slider=True, toggle=False, icon_only=False, event=False,
  3433. full_event=False, emboss=True, index=-1)
  3434. # Direction
  3435. col = layout.column(align=True)
  3436. row = col.row(align=True)
  3437. row.label(text="Direction:")
  3438. row = col.row(align=True)
  3439. row.prop(
  3440. props, "normals_mode", text="Direction", icon='NONE', expand=True,
  3441. slider=False, toggle=False, icon_only=False, event=False,
  3442. full_event=False, emboss=True, index=-1)
  3443. class TISSUE_PT_tessellate_options(Panel):
  3444. bl_space_type = 'PROPERTIES'
  3445. bl_region_type = 'WINDOW'
  3446. bl_context = "data"
  3447. bl_parent_id = "TISSUE_PT_tessellate_object"
  3448. bl_label = " "
  3449. bl_options = {'DEFAULT_CLOSED'}
  3450. @classmethod
  3451. def poll(cls, context):
  3452. try:
  3453. bool_tessellated = context.object.tissue_tessellate.generator != None
  3454. return context.object.type == 'MESH' and bool_tessellated
  3455. except:
  3456. return False
  3457. def draw_header(self, context):
  3458. ob = context.object
  3459. props = ob.tissue_tessellate
  3460. self.layout.prop(props, "merge")
  3461. def draw(self, context):
  3462. ob = context.object
  3463. props = ob.tissue_tessellate
  3464. allowed_obj = ('MESH','CURVE','SURFACE','FONT', 'META')
  3465. try:
  3466. bool_tessellated = props.generator or props.component != None
  3467. ob0 = props.generator
  3468. ob1 = props.component
  3469. except: bool_tessellated = False
  3470. layout = self.layout
  3471. layout.use_property_split = True
  3472. layout.use_property_decorate = False # No animation.
  3473. if bool_tessellated:
  3474. col = layout.column(align=True)
  3475. if props.merge:
  3476. col.prop(props, "merge_thres")
  3477. col.prop(props, "bool_dissolve_seams")
  3478. col.prop(props, "close_mesh")
  3479. if props.close_mesh != 'NONE':
  3480. #row = col.row(align=True)
  3481. col.separator()
  3482. col.prop(props, "open_edges_crease", text="Crease")
  3483. col.prop(props, "cap_material_index", text='Material Index')
  3484. if props.close_mesh == 'BRIDGE':
  3485. col.separator()
  3486. col.prop(props, "bridge_cuts")
  3487. col.prop(props, "bridge_smoothness")
  3488. class TISSUE_PT_tessellate_morphing(Panel):
  3489. bl_space_type = 'PROPERTIES'
  3490. bl_region_type = 'WINDOW'
  3491. bl_context = "data"
  3492. bl_parent_id = "TISSUE_PT_tessellate_object"
  3493. bl_label = "Weight and Morphing"
  3494. bl_options = {'DEFAULT_CLOSED'}
  3495. @classmethod
  3496. def poll(cls, context):
  3497. try:
  3498. bool_tessellated = context.object.tissue_tessellate.generator != None
  3499. return context.object.type == 'MESH' and bool_tessellated
  3500. except:
  3501. return False
  3502. def draw(self, context):
  3503. ob = context.object
  3504. props = ob.tissue_tessellate
  3505. allowed_obj = ('MESH','CURVE','SURFACE','FONT', 'META')
  3506. try:
  3507. bool_tessellated = props.generator or props.component != None
  3508. ob0 = props.generator
  3509. ob1 = props.component
  3510. except: bool_tessellated = False
  3511. layout = self.layout
  3512. if bool_tessellated:
  3513. allow_shapekeys = not props.com_modifiers
  3514. for m in ob0.data.materials:
  3515. try:
  3516. o = bpy.data.objects[m.name]
  3517. allow_multi = True
  3518. try:
  3519. if o.data.shape_keys is None: continue
  3520. elif len(o.data.shape_keys.key_blocks) < 2: continue
  3521. else: allow_shapekeys = not props.com_modifiers
  3522. except: pass
  3523. except: pass
  3524. col = layout.column(align=True)
  3525. #col.label(text="Morphing:")
  3526. row = col.row(align=True)
  3527. col2 = row.column(align=True)
  3528. col2.prop(props, "bool_vertex_group", icon='GROUP_VERTEX')
  3529. #col2.prop_search(props, "vertex_group", props.generator, "vertex_groups")
  3530. try:
  3531. if len(props.generator.vertex_groups) == 0:
  3532. col2.enabled = False
  3533. except:
  3534. col2.enabled = False
  3535. row.separator()
  3536. col2 = row.column(align=True)
  3537. row2 = col2.row(align=True)
  3538. row2.prop(props, "bool_shapekeys", text="Use Shape Keys", icon='SHAPEKEY_DATA')
  3539. row2.enabled = allow_shapekeys
  3540. if not allow_shapekeys:
  3541. col2 = layout.column(align=True)
  3542. row2 = col2.row(align=True)
  3543. row2.label(text="Use Shape Keys is not compatible with Use Modifiers", icon='INFO')
  3544. class TISSUE_PT_tessellate_selective(Panel):
  3545. bl_space_type = 'PROPERTIES'
  3546. bl_region_type = 'WINDOW'
  3547. bl_context = "data"
  3548. bl_parent_id = "TISSUE_PT_tessellate_object"
  3549. bl_label = "Selective"
  3550. bl_options = {'DEFAULT_CLOSED'}
  3551. @classmethod
  3552. def poll(cls, context):
  3553. try:
  3554. bool_tessellated = context.object.tissue_tessellate.generator != None
  3555. return context.object.type == 'MESH' and bool_tessellated
  3556. except:
  3557. return False
  3558. def draw(self, context):
  3559. ob = context.object
  3560. props = ob.tissue_tessellate
  3561. allowed_obj = ('MESH','CURVE','SURFACE','FONT', 'META')
  3562. try:
  3563. bool_tessellated = props.generator or props.component != None
  3564. ob0 = props.generator
  3565. ob1 = props.component
  3566. except: bool_tessellated = False
  3567. layout = self.layout
  3568. if bool_tessellated:
  3569. allow_multi = False
  3570. allow_shapekeys = not props.com_modifiers
  3571. for m in ob0.data.materials:
  3572. try:
  3573. o = bpy.data.objects[m.name]
  3574. allow_multi = True
  3575. try:
  3576. if o.data.shape_keys is None: continue
  3577. elif len(o.data.shape_keys.key_blocks) < 2: continue
  3578. else: allow_shapekeys = not props.com_modifiers
  3579. except: pass
  3580. except: pass
  3581. # LIMITED TESSELLATION
  3582. col = layout.column(align=True)
  3583. #col.label(text="Limited Tessellation:")
  3584. row = col.row(align=True)
  3585. col2 = row.column(align=True)
  3586. col2.prop(props, "bool_selection", text="On selected Faces", icon='RESTRICT_SELECT_OFF')
  3587. row.separator()
  3588. if props.generator.type != 'MESH':
  3589. col2.enabled = False
  3590. col2 = row.column(align=True)
  3591. col2.prop(props, "bool_material_id", icon='MATERIAL_DATA', text="Material ID")
  3592. if props.bool_material_id and not props.bool_multi_components:
  3593. #col2 = row.column(align=True)
  3594. col2.prop(props, "material_id")
  3595. if props.bool_multi_components:
  3596. col2.enabled = False
  3597. col.separator()
  3598. row = col.row(align=True)
  3599. col2 = row.column(align=True)
  3600. col2.prop(props, "bool_multi_components", icon='MOD_TINT')
  3601. if not allow_multi:
  3602. col2.enabled = False
  3603. class TISSUE_PT_tessellate_iterations(Panel):
  3604. bl_space_type = 'PROPERTIES'
  3605. bl_region_type = 'WINDOW'
  3606. bl_context = "data"
  3607. bl_parent_id = "TISSUE_PT_tessellate_object"
  3608. bl_label = "Iterations"
  3609. bl_options = {'DEFAULT_CLOSED'}
  3610. @classmethod
  3611. def poll(cls, context):
  3612. try:
  3613. bool_tessellated = context.object.tissue_tessellate.generator != None
  3614. return context.object.type == 'MESH' and bool_tessellated
  3615. except:
  3616. return False
  3617. def draw(self, context):
  3618. ob = context.object
  3619. props = ob.tissue_tessellate
  3620. allowed_obj = ('MESH','CURVE','SURFACE','FONT', 'META')
  3621. try:
  3622. bool_tessellated = props.generator or props.component != None
  3623. ob0 = props.generator
  3624. ob1 = props.component
  3625. except: bool_tessellated = False
  3626. layout = self.layout
  3627. layout.use_property_split = True
  3628. layout.use_property_decorate = False # No animation.
  3629. if bool_tessellated:
  3630. col = layout.column(align=True)
  3631. row = col.row(align=True)
  3632. #row.label(text='', icon='FILE_REFRESH')
  3633. col.prop(props, 'iterations', text='Repeat')#, icon='FILE_REFRESH')
  3634. if props.iterations > 1 and props.fill_mode == 'PATCH':
  3635. col.separator()
  3636. #row = col.row(align=True)
  3637. col.prop(props, 'patch_subs')
  3638. layout.use_property_split = False
  3639. col = layout.column(align=True)
  3640. #row = col.row(align=True)
  3641. col.label(text='Combine Iterations:')
  3642. row = col.row(align=True)
  3643. row.prop(
  3644. props, "combine_mode", text="Combine:",icon='NONE', expand=True,
  3645. slider=False, toggle=False, icon_only=False, event=False,
  3646. full_event=False, emboss=True, index=-1)
  3647. class tissue_rotate_face_right(Operator):
  3648. bl_idname = "mesh.tissue_rotate_face_right"
  3649. bl_label = "Rotate Faces Right"
  3650. bl_description = "Rotate clockwise selected faces and update tessellated meshes"
  3651. bl_options = {'REGISTER', 'UNDO'}
  3652. @classmethod
  3653. def poll(cls, context):
  3654. try:
  3655. #bool_tessellated = context.object.tissue_tessellate.generator != None
  3656. ob = context.object
  3657. return ob.type == 'MESH' and ob.mode == 'EDIT'# and bool_tessellated
  3658. except:
  3659. return False
  3660. def execute(self, context):
  3661. ob = context.active_object
  3662. me = ob.data
  3663. bm = bmesh.from_edit_mesh(me)
  3664. mesh_select_mode = [sm for sm in context.tool_settings.mesh_select_mode]
  3665. for face in bm.faces:
  3666. if (face.select):
  3667. vs = face.verts[:]
  3668. vs2 = vs[-1:]+vs[:-1]
  3669. material_index = face.material_index
  3670. bm.faces.remove(face)
  3671. f2 = bm.faces.new(vs2)
  3672. f2.select = True
  3673. f2.material_index = material_index
  3674. bm.normal_update()
  3675. # trigger UI update
  3676. bmesh.update_edit_mesh(me)
  3677. ob.select_set(False)
  3678. # update tessellated meshes
  3679. bpy.ops.object.mode_set(mode='OBJECT')
  3680. for o in [obj for obj in bpy.data.objects if
  3681. obj.tissue_tessellate.generator == ob and obj.visible_get()]:
  3682. context.view_layer.objects.active = o
  3683. bpy.ops.object.tissue_update_tessellate()
  3684. o.select_set(False)
  3685. ob.select_set(True)
  3686. context.view_layer.objects.active = ob
  3687. bpy.ops.object.mode_set(mode='EDIT')
  3688. context.tool_settings.mesh_select_mode = mesh_select_mode
  3689. return {'FINISHED'}
  3690. class tissue_rotate_face_left(Operator):
  3691. bl_idname = "mesh.tissue_rotate_face_left"
  3692. bl_label = "Rotate Faces Left"
  3693. bl_description = "Rotate counterclockwise selected faces and update tessellated meshes"
  3694. bl_options = {'REGISTER', 'UNDO'}
  3695. @classmethod
  3696. def poll(cls, context):
  3697. try:
  3698. #bool_tessellated = context.object.tissue_tessellate.generator != None
  3699. ob = context.object
  3700. return ob.type == 'MESH' and ob.mode == 'EDIT'# and bool_tessellated
  3701. except:
  3702. return False
  3703. def execute(self, context):
  3704. ob = context.active_object
  3705. me = ob.data
  3706. bm = bmesh.from_edit_mesh(me)
  3707. mesh_select_mode = [sm for sm in context.tool_settings.mesh_select_mode]
  3708. for face in bm.faces:
  3709. if (face.select):
  3710. vs = face.verts[:]
  3711. vs2 = vs[1:]+vs[:1]
  3712. material_index = face.material_index
  3713. bm.faces.remove(face)
  3714. f2 = bm.faces.new(vs2)
  3715. f2.select = True
  3716. f2.material_index = material_index
  3717. bm.normal_update()
  3718. # trigger UI update
  3719. bmesh.update_edit_mesh(me)
  3720. ob.select_set(False)
  3721. # update tessellated meshes
  3722. bpy.ops.object.mode_set(mode='OBJECT')
  3723. for o in [obj for obj in bpy.data.objects if
  3724. obj.tissue_tessellate.generator == ob and obj.visible_get()]:
  3725. context.view_layer.objects.active = o
  3726. bpy.ops.object.tissue_update_tessellate()
  3727. o.select_set(False)
  3728. ob.select_set(True)
  3729. context.view_layer.objects.active = ob
  3730. bpy.ops.object.mode_set(mode='EDIT')
  3731. context.tool_settings.mesh_select_mode = mesh_select_mode
  3732. return {'FINISHED'}
  3733. def convert_to_frame(ob, props, use_modifiers):
  3734. new_ob = convert_object_to_mesh(ob, use_modifiers, True)
  3735. # create bmesh
  3736. bm = bmesh.new()
  3737. bm.from_mesh(new_ob.data)
  3738. bm.verts.ensure_lookup_table()
  3739. bm.edges.ensure_lookup_table()
  3740. bm.faces.ensure_lookup_table()
  3741. if props.bool_selection:
  3742. original_faces = [f for f in bm.faces if f.select]
  3743. else:
  3744. original_faces = list(bm.faces)
  3745. # detect edge loops
  3746. loops = []
  3747. boundaries_mat = []
  3748. neigh_face_center = []
  3749. face_normals = []
  3750. # append boundary loops
  3751. if props.frame_boundary:
  3752. #selected_edges = [e for e in bm.edges if e.select]
  3753. selected_edges = [e for e in bm.edges if e.is_boundary]
  3754. if len(selected_edges) > 0:
  3755. loop = []
  3756. count = 0
  3757. e0 = selected_edges[0]
  3758. face = e0.link_faces[0]
  3759. boundary_mat = [face.material_index]
  3760. face_center = [face.calc_center_median()]
  3761. loop_normals = [face.normal]
  3762. selected_edges = selected_edges[1:]
  3763. if props.bool_vertex_group:
  3764. n_verts = len(new_ob.data.vertices)
  3765. base_vg = [get_weight(vg,n_verts) for vg in new_ob.vertex_groups]
  3766. '''
  3767. base_vg = []
  3768. for vg in new_ob.vertex_groups:
  3769. vertex_group = []
  3770. for v in bm.verts:
  3771. try:
  3772. vertex_group.append(vg.weight(v.index))
  3773. except:
  3774. vertex_group.append(0)
  3775. base_vg.append(vertex_group)
  3776. '''
  3777. while True:
  3778. new_vert = None
  3779. face = None
  3780. for e1 in selected_edges:
  3781. if e1.verts[0] in e0.verts: new_vert = e1.verts[1]
  3782. elif e1.verts[1] in e0.verts: new_vert = e1.verts[0]
  3783. if new_vert != None:
  3784. if len(loop)==0:
  3785. loop = [v for v in e1.verts if v != new_vert]
  3786. loop.append(new_vert)
  3787. e0 = e1
  3788. face = e0.link_faces[0]
  3789. boundary_mat.append(face.material_index)
  3790. face_center.append(face.calc_center_median())
  3791. loop_normals.append(face.normal)
  3792. selected_edges.remove(e0)
  3793. break
  3794. if new_vert == None:
  3795. try:
  3796. loops.append(loop)
  3797. loop = []
  3798. e0 = selected_edges[0]
  3799. selected_edges = selected_edges[1:]
  3800. boundaries_mat.append(boundary_mat)
  3801. neigh_face_center.append(face_center)
  3802. face_normals.append(loop_normals)
  3803. face = e0.link_faces[0]
  3804. boundary_mat = [face.material_index]
  3805. face_center = [face.calc_center_median()]
  3806. loop_normals = [face.normal]
  3807. except: break
  3808. boundaries_mat.append(boundary_mat)
  3809. neigh_face_center.append(face_center)
  3810. face_normals.append(loop_normals)
  3811. # compute boundary frames
  3812. new_faces = []
  3813. vert_ids = []
  3814. # append regular faces
  3815. for f in original_faces:#bm.faces:
  3816. loop = list(f.verts)
  3817. loops.append(loop)
  3818. boundaries_mat.append([f.material_index for v in loop])
  3819. face_normals.append([f.normal for v in loop])
  3820. # calc areas for relative frame mode
  3821. if props.frame_mode == 'RELATIVE':
  3822. verts_area = []
  3823. for v in bm.verts:
  3824. linked_faces = v.link_faces
  3825. if len(linked_faces) > 0:
  3826. area = sum([sqrt(f.calc_area())/len(f.verts) for f in v.link_faces])*2
  3827. area /= len(linked_faces)
  3828. else: area = 0
  3829. verts_area.append(area)
  3830. for loop_index, loop in enumerate(loops):
  3831. is_boundary = loop_index < len(neigh_face_center)
  3832. materials = boundaries_mat[loop_index]
  3833. new_loop = []
  3834. loop_ext = [loop[-1]] + loop + [loop[0]]
  3835. # calc tangents
  3836. tangents = []
  3837. for i in range(len(loop)):
  3838. # vertices
  3839. vert0 = loop_ext[i]
  3840. vert = loop_ext[i+1]
  3841. vert1 = loop_ext[i+2]
  3842. # edge vectors
  3843. vec0 = (vert0.co - vert.co).normalized()
  3844. vec1 = (vert.co - vert1.co).normalized()
  3845. # tangent
  3846. _vec1 = -vec1
  3847. _vec0 = -vec0
  3848. ang = (pi - vec0.angle(vec1))/2
  3849. normal = face_normals[loop_index][i]
  3850. tan0 = normal.cross(vec0)
  3851. tan1 = normal.cross(vec1)
  3852. tangent = (tan0 + tan1).normalized()/sin(ang)*props.frame_thickness
  3853. tangents.append(tangent)
  3854. # calc correct direction for boundaries
  3855. mult = -1
  3856. if is_boundary:
  3857. dir_val = 0
  3858. for i in range(len(loop)):
  3859. surf_point = neigh_face_center[loop_index][i]
  3860. tangent = tangents[i]
  3861. vert = loop_ext[i+1]
  3862. dir_val += tangent.dot(vert.co - surf_point)
  3863. if dir_val > 0: mult = 1
  3864. # add vertices
  3865. for i in range(len(loop)):
  3866. vert = loop_ext[i+1]
  3867. if props.frame_mode == 'RELATIVE': area = verts_area[vert.index]
  3868. else: area = 1
  3869. new_co = vert.co + tangents[i] * mult * area
  3870. # add vertex
  3871. new_vert = bm.verts.new(new_co)
  3872. new_loop.append(new_vert)
  3873. vert_ids.append(vert.index)
  3874. new_loop.append(new_loop[0])
  3875. # add faces
  3876. materials += [materials[0]]
  3877. for i in range(len(loop)):
  3878. v0 = loop_ext[i+1]
  3879. v1 = loop_ext[i+2]
  3880. v2 = new_loop[i+1]
  3881. v3 = new_loop[i]
  3882. face_verts = [v1,v0,v3,v2]
  3883. if mult == -1: face_verts = [v0,v1,v2,v3]
  3884. new_face = bm.faces.new(face_verts)
  3885. new_face.material_index = materials[i+1] + props.frame_boundary_mat
  3886. new_face.select = True
  3887. new_faces.append(new_face)
  3888. # fill frame
  3889. if props.fill_frame and not is_boundary:
  3890. n_verts = len(new_loop)-1
  3891. loop_center = Vector((0,0,0))
  3892. for v in new_loop[1:]: loop_center += v.co
  3893. loop_center /= n_verts
  3894. center = bm.verts.new(loop_center)
  3895. for i in range(n_verts):
  3896. v0 = new_loop[i+1]
  3897. v1 = new_loop[i]
  3898. face_verts = [v1,v0,center]
  3899. new_face = bm.faces.new(face_verts)
  3900. new_face.material_index = materials[i] + props.frame_boundary_mat
  3901. new_face.select = True
  3902. new_faces.append(new_face)
  3903. bpy.ops.object.mode_set(mode='OBJECT')
  3904. #for f in bm.faces: f.select_set(f not in new_faces)
  3905. for f in original_faces: bm.faces.remove(f)
  3906. bm.to_mesh(new_ob.data)
  3907. # propagate vertex groups
  3908. if props.bool_vertex_group:
  3909. base_vg = []
  3910. for vg in new_ob.vertex_groups:
  3911. vertex_group = []
  3912. for v in bm.verts:
  3913. try:
  3914. vertex_group.append(vg.weight(v.index))
  3915. except:
  3916. vertex_group.append(0)
  3917. base_vg.append(vertex_group)
  3918. new_vert_ids = range(len(bm.verts)-len(vert_ids),len(bm.verts))
  3919. for vg_id, vg in enumerate(new_ob.vertex_groups):
  3920. for ii, jj in zip(vert_ids, new_vert_ids):
  3921. vg.add([jj], base_vg[vg_id][ii], 'REPLACE')
  3922. new_ob.data.update()
  3923. return new_ob
  3924. def convert_to_fan(ob, props, use_modifiers):
  3925. new_ob = convert_object_to_mesh(ob, use_modifiers, True)
  3926. # make base object selected and active
  3927. for o in bpy.context.view_layer.objects: o.select_set(False)
  3928. new_ob.select_set(True)
  3929. bpy.context.view_layer.objects.active = new_ob
  3930. sk_index0 = new_ob.active_shape_key_index
  3931. new_ob.active_shape_key_index = 0
  3932. bpy.ops.object.mode_set(mode='EDIT')
  3933. bpy.ops.mesh.select_mode(type='FACE')
  3934. if not props.bool_selection:
  3935. bpy.ops.mesh.select_all(action='SELECT')
  3936. bpy.ops.mesh.poke()
  3937. bpy.ops.object.mode_set(mode='OBJECT')
  3938. return new_ob