| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280 |
- # ##### BEGIN GPL LICENSE BLOCK #####
- #
- # This program is free software; you can redistribute it and/or
- # modify it under the terms of the GNU General Public License
- # as published by the Free Software Foundation; either version 2
- # of the License, or (at your option) any later version.
- #
- # This program is distributed in the hope that it will be useful,
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- # GNU General Public License for more details.
- #
- # You should have received a copy of the GNU General Public License
- # along with this program; if not, write to the Free Software Foundation,
- # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- #
- # ##### END GPL LICENSE BLOCK #####
-
- # ---------------------------- ADAPTIVE DUPLIFACES --------------------------- #
- # ------------------------------- version 0.84 ------------------------------- #
- # #
- # Creates duplicates of selected mesh to active morphing the shape according #
- # to target faces. #
- # #
- # (c) Alessandro Zomparelli #
- # (2017) #
- # #
- # http://www.co-de-it.com/ #
- # #
- # ############################################################################ #
-
-
- import bpy
- from bpy.types import (
- Operator,
- Panel,
- PropertyGroup,
- )
- from bpy.props import (
- BoolProperty,
- EnumProperty,
- FloatProperty,
- IntProperty,
- StringProperty,
- PointerProperty
- )
- from mathutils import Vector
- import numpy as np
- from math import *
- import random, time, copy
- import bmesh
- from .utils import *
-
- def anim_tessellate_active(self, context):
- ob = context.object
- props = ob.tissue_tessellate
- if not (props.bool_lock or props.bool_hold):
- try:
- props.generator.name
- props.component.name
- bpy.ops.object.tissue_update_tessellate()
- except: pass
-
- def anim_tessellate_object(ob):
- try:
- #bpy.context.view_layer.objects.active = ob
- bpy.ops.object.tissue_update_tessellate()
- except:
- return None
-
- #from bpy.app.handlers import persistent
-
- def anim_tessellate(scene):
- try:
- active_object = bpy.context.object
- old_mode = bpy.context.object.mode
- selected_objects = bpy.context.selected_objects
- except: active_object = old_mode = selected_objects = None
- if old_mode in ('OBJECT', 'PAINT_WEIGHT'):
- update_objects = []
- for ob in scene.objects:
- if ob.tissue_tessellate.bool_run and not ob.tissue_tessellate.bool_lock:
- if ob not in update_objects: update_objects.append(ob)
- update_objects = list(reversed(update_dependencies(ob, update_objects)))
- for ob in update_objects:
- override = {'object': ob}
- '''
- win = bpy.data.window_managers[0].windows[0]#bpy.context.window
- scr = win.screen
- areas3d = [area for area in scr.areas if area.type == 'VIEW_3D']
- region = [region for region in areas3d[0].regions if region.type == 'WINDOW']
- override = {
- 'window':win,
- 'screen':scr,
- 'area' :areas3d[0],
- 'region':region[0],
- 'scene' :scene,
- 'object': ob
- }
- '''
- print(override)
- bpy.ops.object.tissue_update_tessellate(override)
- # restore selected objects
- if old_mode != None:
- for o in scene.objects:
- if not o.hide_viewport: o.select_set(o in selected_objects)
- bpy.context.view_layer.objects.active = active_object
- bpy.ops.object.mode_set(mode=old_mode)
- return
-
-
- def set_tessellate_handler(self, context):
- old_handlers = []
- blender_handlers = bpy.app.handlers.frame_change_post
- for h in blender_handlers:
- if "anim_tessellate" in str(h):
- old_handlers.append(h)
- for h in old_handlers: blender_handlers.remove(h)
- for o in context.scene.objects:
- if o.tissue_tessellate.bool_run:
- blender_handlers.append(anim_tessellate)
- break
- return
-
- class tissue_tessellate_prop(PropertyGroup):
- bool_lock : BoolProperty(
- name="Lock",
- description="Prevent automatic update on settings changes or if other objects have it in the hierarchy.",
- default=False
- )
- bool_hold : BoolProperty(
- name="Hold",
- description="Wait...",
- default=False
- )
- bool_dependencies : BoolProperty(
- name="Update Dependencies",
- description="Automatically updates base and components as well, if results of other tessellations",
- default=False
- )
- bool_run : BoolProperty(
- name="Animatable Tessellation",
- description="Automatically recompute the tessellation when the frame is changed. Currently is not working during Render Animation",
- default = False,
- update = set_tessellate_handler
- )
- zscale : FloatProperty(
- name="Scale", default=1, soft_min=0, soft_max=10,
- description="Scale factor for the component thickness",
- update = anim_tessellate_active
- )
- scale_mode : EnumProperty(
- items=(
- ('CONSTANT', "Constant", "Uniform thinkness"),
- ('ADAPTIVE', "Relative", "Preserve component's proportions")
- ),
- default='ADAPTIVE',
- name="Z-Scale according to faces size",
- update = anim_tessellate_active
- )
- offset : FloatProperty(
- name="Surface Offset",
- default=1,
- min=-1,
- max=1,
- soft_min=-1,
- soft_max=1,
- description="Surface offset",
- update = anim_tessellate_active
- )
- mode : EnumProperty(
- items=(
- ('BOUNDS', "Bounds", "The component fits automatically the size of the target face"),
- ('LOCAL', "Local", "Based on Local coordinates, from 0 to 1"),
- ('GLOBAL', 'Global', "Based on Global coordinates, from 0 to 1")),
- default='BOUNDS',
- name="Component Mode",
- update = anim_tessellate_active
- )
- rotation_mode : EnumProperty(
- items=(('RANDOM', "Random", "Random faces rotation"),
- ('UV', "Active UV", "Rotate according to UV coordinates"),
- ('WEIGHT', "Active Weight", "Rotate according to Vertex Group gradient"),
- ('DEFAULT', "Default", "Default rotation")),
- default='DEFAULT',
- name="Component Rotation",
- update = anim_tessellate_active
- )
- rotation_direction : EnumProperty(
- items=(('ORTHO', "Orthogonal", "Component main directions in XY"),
- ('DIAG', "Diagonal", "Component main direction aligned with diagonal")),
- default='ORTHO',
- name="Direction",
- update = anim_tessellate_active
- )
- rotation_shift : IntProperty(
- name="Shift",
- default=0,
- soft_min=0,
- soft_max=3,
- description="Shift components rotation",
- update = anim_tessellate_active
- )
- fill_mode : EnumProperty(
- items=(
- ('QUAD', 'Quad', 'Regular quad tessellation. Uses only 3 or 4 vertices'),
- ('FAN', 'Fan', 'Radial tessellation for polygonal faces'),
- ('PATCH', 'Patch', 'Curved tessellation according to the last ' +
- 'Subsurf\n(or Multires) modifiers. Works only with 4 sides ' +
- 'patches.\nAfter the last Subsurf (or Multires) only ' +
- 'deformation\nmodifiers can be used'),
- ('FRAME', 'Frame', 'Essellation along the edges of each face')),
- default='QUAD',
- name="Fill Mode",
- update = anim_tessellate_active
- )
- combine_mode : EnumProperty(
- items=(
- ('LAST', 'Last', 'Show only the last iteration'),
- ('UNUSED', 'Unused', 'Combine each iteration with the unused faces of the previous iteration. Used for branching systems'),
- ('ALL', 'All', 'Combine the result of all iterations')),
- default='LAST',
- name="Combine Mode",
- update = anim_tessellate_active
- )
- gen_modifiers : BoolProperty(
- name="Generator Modifiers",
- default=False,
- description="Apply Modifiers and Shape Keys to the base object",
- update = anim_tessellate_active
- )
- com_modifiers : BoolProperty(
- name="Component Modifiers",
- default=False,
- description="Apply Modifiers and Shape Keys to the component object",
- update = anim_tessellate_active
- )
- merge : BoolProperty(
- name="Merge",
- default=False,
- description="Merge vertices in adjacent duplicates",
- update = anim_tessellate_active
- )
- merge_thres : FloatProperty(
- name="Distance",
- default=0.001,
- soft_min=0,
- soft_max=10,
- description="Limit below which to merge vertices",
- update = anim_tessellate_active
- )
- generator : PointerProperty(
- type=bpy.types.Object,
- name="",
- description="Base object for the tessellation",
- update = anim_tessellate_active
- )
- component : PointerProperty(
- type=bpy.types.Object,
- name="",
- description="Component object for the tessellation",
- #default="",
- update = anim_tessellate_active
- )
- bool_random : BoolProperty(
- name="Randomize",
- default=False,
- description="Randomize component rotation",
- update = anim_tessellate_active
- )
- random_seed : IntProperty(
- name="Seed",
- default=0,
- soft_min=0,
- soft_max=10,
- description="Random seed",
- update = anim_tessellate_active
- )
- bool_vertex_group : BoolProperty(
- name="Map Vertex Group",
- default=False,
- description="Transfer all Vertex Groups from Base object",
- update = anim_tessellate_active
- )
- bool_selection : BoolProperty(
- name="On selected Faces",
- default=False,
- description="Create Tessellation only on selected faces",
- update = anim_tessellate_active
- )
- bool_shapekeys : BoolProperty(
- name="Use Shape Keys",
- default=False,
- description="Transfer Component's Shape Keys. If the name of Vertex "
- "Groups and Shape Keys are the same, they will be "
- "automatically combined",
- update = anim_tessellate_active
- )
- bool_smooth : BoolProperty(
- name="Smooth Shading",
- default=False,
- description="Output faces with smooth shading rather than flat shaded",
- update = anim_tessellate_active
- )
- bool_materials : BoolProperty(
- name="Transfer Materials",
- default=False,
- description="Preserve component's materials",
- update = anim_tessellate_active
- )
- bool_material_id : BoolProperty(
- name="Tessellation on Material ID",
- default=False,
- description="Apply the component only on the selected Material",
- update = anim_tessellate_active
- )
- material_id : IntProperty(
- name="Material ID",
- default=0,
- min=0,
- description="Material ID",
- update = anim_tessellate_active
- )
- bool_dissolve_seams : BoolProperty(
- name="Dissolve Seams",
- default=False,
- description="Dissolve all seam edges",
- update = anim_tessellate_active
- )
- iterations : IntProperty(
- name="Iterations",
- default=1,
- min=1,
- soft_max=5,
- description="Automatically repeat the Tessellation using the "
- + "generated geometry as new base object.\nUsefull for "
- + "for branching systems. Dangerous!",
- update = anim_tessellate_active
- )
- bool_combine : BoolProperty(
- name="Combine unused",
- default=False,
- description="Combine the generated geometry with unused faces",
- update = anim_tessellate_active
- )
- bool_advanced : BoolProperty(
- name="Advanced Settings",
- default=False,
- description="Show more settings"
- )
- normals_mode : EnumProperty(
- items=(
- ('VERTS', 'Normals', 'Consistent direction based on vertices normal'),
- ('FACES', 'Individual Faces', 'Based on individual faces normal'),
- ('CUSTOM', 'Custom', "According to Base object's shape keys")),
- default='VERTS',
- name="Direction",
- update = anim_tessellate_active
- )
- bool_multi_components : BoolProperty(
- name="Multi Components",
- default=False,
- description="Combine different components according to materials name",
- update = anim_tessellate_active
- )
- error_message : StringProperty(
- name="Error Message",
- default=""
- )
- warning_message : StringProperty(
- name="Warning Message",
- default=""
- )
- bounds_x : EnumProperty(
- items=(
- ('EXTEND', 'Extend', 'Default X coordinates'),
- ('CLIP', 'Clip', 'Trim out of bounds in X direction'),
- ('CYCLIC', 'Cyclic', 'Cyclic components in X direction')),
- default='EXTEND',
- name="Bounds X",
- update = anim_tessellate_active
- )
- bounds_y : EnumProperty(
- items=(
- ('EXTEND', 'Extend', 'Default Y coordinates'),
- ('CLIP', 'Clip', 'Trim out of bounds in Y direction'),
- ('CYCLIC', 'Cyclic', 'Cyclic components in Y direction')),
- default='EXTEND',
- name="Bounds Y",
- update = anim_tessellate_active
- )
- close_mesh : EnumProperty(
- items=(
- ('NONE', 'None', 'Keep the mesh open'),
- ('CAP', 'Cap Holes', 'Automatically cap open loops'),
- ('BRIDGE', 'Bridge Loops', 'Automatically bridge loop pairs')),
- default='NONE',
- name="Close Mesh",
- update = anim_tessellate_active
- )
- cap_faces : BoolProperty(
- name="Cap Holes",
- default=False,
- description="Cap open edges loops",
- update = anim_tessellate_active
- )
- frame_boundary : BoolProperty(
- name="Frame Boundary",
- default=False,
- description="Support face boundaries",
- update = anim_tessellate_active
- )
- fill_frame : BoolProperty(
- name="Fill Frame",
- default=False,
- description="Fill inner faces with Fan tessellation",
- update = anim_tessellate_active
- )
- frame_boundary_mat : IntProperty(
- name="Material Offset",
- default=0,
- description="Material Offset for boundaries",
- update = anim_tessellate_active
- )
- fill_frame_mat : IntProperty(
- name="Material Offset",
- default=0,
- description="Material Offset for inner faces",
- update = anim_tessellate_active
- )
- open_edges_crease : FloatProperty(
- name="Open Edges Crease",
- default=0,
- min=0,
- max=1,
- description="Automatically set crease for open edges",
- update = anim_tessellate_active
- )
- bridge_smoothness : FloatProperty(
- name="Smoothness",
- default=1,
- min=0,
- max=1,
- description="Bridge Smoothness",
- update = anim_tessellate_active
- )
- frame_thickness : FloatProperty(
- name="Frame Thickness",
- default=0.2,
- min=0,
- soft_max=2,
- description="Frame Thickness",
- update = anim_tessellate_active
- )
- frame_mode : EnumProperty(
- items=(
- ('CONSTANT', 'Constant', 'Even thickness'),
- ('RELATIVE', 'Relative', 'Frame offset depends on face areas')),
- default='CONSTANT',
- name="Offset",
- update = anim_tessellate_active
- )
- bridge_cuts : IntProperty(
- name="Cuts",
- default=0,
- min=0,
- max=20,
- description="Bridge Cuts",
- update = anim_tessellate_active
- )
- cap_material_index : IntProperty(
- name="Material",
- default=0,
- min=0,
- description="Material index for the cap/bridge faces",
- update = anim_tessellate_active
- )
- patch_subs : IntProperty(
- name="Patch Subdivisions",
- default=1,
- min=0,
- description="Subdivisions levels for Patch tessellation after the first iteration",
- update = anim_tessellate_active
- )
-
- def store_parameters(operator, ob):
- ob.tissue_tessellate.bool_hold = True
- ob.tissue_tessellate.bool_lock = operator.bool_lock
- ob.tissue_tessellate.bool_dependencies = operator.bool_dependencies
- ob.tissue_tessellate.generator = bpy.data.objects[operator.generator]
- ob.tissue_tessellate.component = bpy.data.objects[operator.component]
- ob.tissue_tessellate.zscale = operator.zscale
- ob.tissue_tessellate.offset = operator.offset
- ob.tissue_tessellate.gen_modifiers = operator.gen_modifiers
- ob.tissue_tessellate.com_modifiers = operator.com_modifiers
- ob.tissue_tessellate.mode = operator.mode
- ob.tissue_tessellate.rotation_mode = operator.rotation_mode
- ob.tissue_tessellate.rotation_shift = operator.rotation_shift
- ob.tissue_tessellate.rotation_direction = operator.rotation_direction
- ob.tissue_tessellate.merge = operator.merge
- ob.tissue_tessellate.merge_thres = operator.merge_thres
- ob.tissue_tessellate.scale_mode = operator.scale_mode
- ob.tissue_tessellate.bool_random = operator.bool_random
- ob.tissue_tessellate.random_seed = operator.random_seed
- ob.tissue_tessellate.fill_mode = operator.fill_mode
- ob.tissue_tessellate.bool_vertex_group = operator.bool_vertex_group
- ob.tissue_tessellate.bool_selection = operator.bool_selection
- ob.tissue_tessellate.bool_shapekeys = operator.bool_shapekeys
- ob.tissue_tessellate.bool_smooth = operator.bool_smooth
- ob.tissue_tessellate.bool_materials = operator.bool_materials
- ob.tissue_tessellate.bool_material_id = operator.bool_material_id
- ob.tissue_tessellate.material_id = operator.material_id
- ob.tissue_tessellate.bool_dissolve_seams = operator.bool_dissolve_seams
- ob.tissue_tessellate.iterations = operator.iterations
- ob.tissue_tessellate.bool_advanced = operator.bool_advanced
- ob.tissue_tessellate.normals_mode = operator.normals_mode
- ob.tissue_tessellate.bool_combine = operator.bool_combine
- ob.tissue_tessellate.bool_multi_components = operator.bool_multi_components
- ob.tissue_tessellate.combine_mode = operator.combine_mode
- ob.tissue_tessellate.bounds_x = operator.bounds_x
- ob.tissue_tessellate.bounds_y = operator.bounds_y
- ob.tissue_tessellate.cap_faces = operator.cap_faces
- ob.tissue_tessellate.close_mesh = operator.close_mesh
- ob.tissue_tessellate.bridge_cuts = operator.bridge_cuts
- ob.tissue_tessellate.bridge_smoothness = operator.bridge_smoothness
- ob.tissue_tessellate.frame_thickness = operator.frame_thickness
- ob.tissue_tessellate.frame_mode = operator.frame_mode
- ob.tissue_tessellate.frame_boundary = operator.frame_boundary
- ob.tissue_tessellate.fill_frame = operator.fill_frame
- ob.tissue_tessellate.frame_boundary_mat = operator.frame_boundary_mat
- ob.tissue_tessellate.fill_frame_mat = operator.fill_frame_mat
- ob.tissue_tessellate.cap_material_index = operator.cap_material_index
- ob.tissue_tessellate.patch_subs = operator.patch_subs
- ob.tissue_tessellate.bool_hold = False
- return ob
-
- def load_parameters(operator, ob):
- operator.bool_lock = ob.tissue_tessellate.bool_lock
- operator.bool_dependencies = ob.tissue_tessellate.bool_dependencies
- operator.generator = ob.tissue_tessellate.generator.name
- operator.component = ob.tissue_tessellate.component.name
- operator.zscale = ob.tissue_tessellate.zscale
- operator.offset = ob.tissue_tessellate.offset
- operator.gen_modifiers = ob.tissue_tessellate.gen_modifiers
- operator.com_modifiers = ob.tissue_tessellate.com_modifiers
- operator.mode = ob.tissue_tessellate.mode
- operator.rotation_mode = ob.tissue_tessellate.rotation_mode
- operator.rotation_shift = ob.tissue_tessellate.rotation_shift
- operator.rotation_direction = ob.tissue_tessellate.rotation_direction
- operator.merge = ob.tissue_tessellate.merge
- operator.merge_thres = ob.tissue_tessellate.merge_thres
- operator.scale_mode = ob.tissue_tessellate.scale_mode
- operator.bool_random = ob.tissue_tessellate.bool_random
- operator.random_seed = ob.tissue_tessellate.random_seed
- operator.fill_mode = ob.tissue_tessellate.fill_mode
- operator.bool_vertex_group = ob.tissue_tessellate.bool_vertex_group
- operator.bool_selection = ob.tissue_tessellate.bool_selection
- operator.bool_shapekeys = ob.tissue_tessellate.bool_shapekeys
- operator.bool_smooth = ob.tissue_tessellate.bool_smooth
- operator.bool_materials = ob.tissue_tessellate.bool_materials
- operator.bool_material_id = ob.tissue_tessellate.bool_material_id
- operator.material_id = ob.tissue_tessellate.material_id
- operator.bool_dissolve_seams = ob.tissue_tessellate.bool_dissolve_seams
- operator.iterations = ob.tissue_tessellate.iterations
- operator.bool_advanced = ob.tissue_tessellate.bool_advanced
- operator.normals_mode = ob.tissue_tessellate.normals_mode
- operator.bool_combine = ob.tissue_tessellate.bool_combine
- operator.bool_multi_components = ob.tissue_tessellate.bool_multi_components
- operator.combine_mode = ob.tissue_tessellate.combine_mode
- operator.bounds_x = ob.tissue_tessellate.bounds_x
- operator.bounds_y = ob.tissue_tessellate.bounds_y
- operator.cap_faces = ob.tissue_tessellate.cap_faces
- operator.close_mesh = ob.tissue_tessellate.close_mesh
- operator.bridge_cuts = ob.tissue_tessellate.bridge_cuts
- operator.bridge_smoothness = ob.tissue_tessellate.bridge_smoothness
- operator.cap_material_index = ob.tissue_tessellate.cap_material_index
- operator.patch_subs = ob.tissue_tessellate.patch_subs
- operator.frame_boundary = ob.tissue_tessellate.frame_boundary
- operator.fill_frame = ob.tissue_tessellate.fill_frame
- operator.frame_boundary_mat = ob.tissue_tessellate.frame_boundary_mat
- operator.fill_frame_mat = ob.tissue_tessellate.fill_frame_mat
- operator.frame_thickness = ob.tissue_tessellate.frame_thickness
- operator.frame_mode = ob.tissue_tessellate.frame_mode
- return ob
-
- def tessellate_patch(_ob0, _ob1, offset, zscale, com_modifiers, mode,
- scale_mode, rotation_mode, rotation_shift, rand_seed, bool_vertex_group,
- bool_selection, bool_shapekeys, bool_material_id, material_id,
- normals_mode, bounds_x, bounds_y):
- random.seed(rand_seed)
-
- if normals_mode == 'CUSTOM':
- if _ob0.data.shape_keys != None:
- ob0_sk = convert_object_to_mesh(_ob0)
- me0_sk = ob0_sk.data
- key_values0 = [sk.value for sk in _ob0.data.shape_keys.key_blocks]
- for sk in _ob0.data.shape_keys.key_blocks: sk.value = 0
- else: normals_mode = 'VERTS'
-
- ob0 = convert_object_to_mesh(_ob0)
- me0 = ob0.data
-
- # base normals
- normals0 = []
- if normals_mode == 'CUSTOM':
- for sk, val in zip(_ob0.data.shape_keys.key_blocks, key_values0): sk.value = val
- for v0, v1 in zip(ob0.data.vertices, me0_sk.vertices):
- normals0.append(v1.co - v0.co)
- bpy.data.objects.remove(ob0_sk)
- else:
- ob0.data.update()
- normals0 = [v.normal for v in ob0.data.vertices]
-
- # ob0 = convert_object_to_mesh(_ob0)
- ob0.name = _ob0.name + "_apply_mod"
- me0 = _ob0.data
-
- # Check if zero faces are selected
- if _ob0.type == 'MESH':
- bool_cancel = True
- for p in me0.polygons:
- check_sel = check_mat = False
- if not bool_selection or p.select: check_sel = True
- if not bool_material_id or p.material_index == material_id: check_mat = True
- if check_sel and check_mat:
- bool_cancel = False
- break
- if bool_cancel:
- bpy.data.meshes.remove(ob0.data)
- #bpy.data.objects.remove(ob0)
- return 0
-
- levels = 0
- sculpt_levels = 0
- render_levels = 0
- bool_multires = False
- multires_name = ""
- not_allowed = ['FLUID_SIMULATION', 'ARRAY', 'BEVEL', 'BOOLEAN', 'BUILD',
- 'DECIMATE', 'EDGE_SPLIT', 'MASK', 'MIRROR', 'REMESH',
- 'SCREW', 'SOLIDIFY', 'TRIANGULATE', 'WIREFRAME', 'SKIN',
- 'EXPLODE', 'PARTICLE_INSTANCE', 'PARTICLE_SYSTEM', 'SMOKE']
- modifiers0 = list(_ob0.modifiers)#[m for m in ob0.modifiers]
- show_modifiers = [m.show_viewport for m in _ob0.modifiers]
- show_modifiers.reverse()
- modifiers0.reverse()
- for m in modifiers0:
- visible = m.show_viewport
- if not visible: continue
- #m.show_viewport = False
- if m.type in ('SUBSURF', 'MULTIRES') and visible:
- levels = m.levels
- multires_name = m.name
- if m.type == 'MULTIRES':
- bool_multires = True
- multires_name = m.name
- sculpt_levels = m.sculpt_levels
- render_levels = m.render_levels
- else: bool_multires = False
- break
- elif m.type in not_allowed:
- bpy.data.meshes.remove(ob0.data)
- #bpy.data.meshes.remove(me0)
- return "modifiers_error"
-
- before = _ob0.copy()
- before.name = _ob0.name + "_before_subs"
- bpy.context.collection.objects.link(before)
- #if ob0.type == 'MESH': before.data = me0
- before_mod = list(before.modifiers)
- before_mod.reverse()
- for m in before_mod:
- if m.type in ('SUBSURF', 'MULTIRES') and m.show_viewport:
- before.modifiers.remove(m)
- break
- else: before.modifiers.remove(m)
-
- before_subsurf = simple_to_mesh(before)
-
- before_bm = bmesh.new()
- before_bm.from_mesh(before_subsurf)
- before_bm.faces.ensure_lookup_table()
- before_bm.edges.ensure_lookup_table()
- before_bm.verts.ensure_lookup_table()
-
- error = ""
- for f in before_bm.faces:
- if len(f.loops) != 4:
- error = "topology_error"
- break
- for e in before_bm.edges:
- if len(e.link_faces) == 0:
- error = "wires_error"
- break
- for v in before_bm.verts:
- if len(v.link_faces) == 0:
- error = "verts_error"
- break
- if error != "":
- bpy.data.meshes.remove(ob0.data)
- #bpy.data.meshes.remove(me0)
- bpy.data.meshes.remove(before_subsurf)
- bpy.data.objects.remove(before)
- return error
-
- me0 = ob0.data
- verts0 = me0.vertices # Collect generator vertices
-
- if com_modifiers or _ob1.type != 'MESH': bool_shapekeys = False
-
- # set Shape Keys to zero
- if bool_shapekeys or not com_modifiers:
- try:
- original_key_values = []
- for sk in _ob1.data.shape_keys.key_blocks:
- original_key_values.append(sk.value)
- sk.value = 0
- except:
- bool_shapekeys = False
-
- if not com_modifiers and not bool_shapekeys:
- mod_visibility = []
- for m in _ob1.modifiers:
- mod_visibility.append(m.show_viewport)
- m.show_viewport = False
- com_modifiers = True
-
- ob1 = convert_object_to_mesh(_ob1, com_modifiers, False)
- me1 = ob1.data
-
- if mode != 'BOUNDS':
- ob1.active_shape_key_index = 0
- # Bound X
- if bounds_x != 'EXTEND':
- if mode == 'GLOBAL':
- planes_co = ((0,0,0),(1,1,1))
- plane_no = (1,0,0)
- if mode == 'LOCAL':
- planes_co = (ob1.matrix_world @ Vector((0,0,0)), ob1.matrix_world @ Vector((1,0,0)))
- plane_no = planes_co[0]-planes_co[1]
- bpy.ops.object.mode_set(mode='EDIT')
- for co in planes_co:
- bpy.ops.mesh.select_all(action='SELECT')
- bpy.ops.mesh.bisect(plane_co=co, plane_no=plane_no)
- bpy.ops.mesh.mark_seam()
- bpy.ops.object.mode_set(mode='OBJECT')
- _faces = ob1.data.polygons
- if mode == 'GLOBAL':
- for f in [f for f in _faces if (ob1.matrix_world @ f.center).x > 1]:
- f.select = True
- for f in [f for f in _faces if (ob1.matrix_world @ f.center).x < 0]:
- f.select = True
- else:
- for f in [f for f in _faces if f.center.x > 1]:
- f.select = True
- for f in [f for f in _faces if f.center.x < 0]:
- f.select = True
- bpy.ops.object.mode_set(mode='EDIT')
- bpy.ops.mesh.select_mode(type='FACE')
- if bounds_x == 'CLIP':
- bpy.ops.mesh.delete(type='FACE')
- bpy.ops.object.mode_set(mode='OBJECT')
- if bounds_x == 'CYCLIC':
- bpy.ops.mesh.split()
- bpy.ops.object.mode_set(mode='OBJECT')
- # Bound Y
- if bounds_y != 'EXTEND':
- if mode == 'GLOBAL':
- planes_co = ((0,0,0),(1,1,1))
- plane_no = (0,1,0)
- if mode == 'LOCAL':
- planes_co = (ob1.matrix_world @ Vector((0,0,0)), ob1.matrix_world @ Vector((0,1,0)))
- plane_no = planes_co[0]-planes_co[1]
- bpy.ops.object.mode_set(mode='EDIT')
- for co in planes_co:
- bpy.ops.mesh.select_all(action='SELECT')
- bpy.ops.mesh.bisect(plane_co=co, plane_no=plane_no)
- bpy.ops.mesh.mark_seam()
- bpy.ops.object.mode_set(mode='OBJECT')
- _faces = ob1.data.polygons
- if mode == 'GLOBAL':
- for f in [f for f in _faces if (ob1.matrix_world @ f.center).y > 1]:
- f.select = True
- for f in [f for f in _faces if (ob1.matrix_world @ f.center).y < 0]:
- f.select = True
- else:
- for f in [f for f in _faces if f.center.y > 1]:
- f.select = True
- for f in [f for f in _faces if f.center.y < 0]:
- f.select = True
-
- bpy.ops.object.mode_set(mode='EDIT')
- bpy.ops.mesh.select_mode(type='FACE')
- if bounds_y == 'CLIP':
- bpy.ops.mesh.delete(type='FACE')
- bpy.ops.object.mode_set(mode='OBJECT')
- if bounds_y == 'CYCLIC':
- bpy.ops.mesh.split()
- bpy.ops.object.mode_set(mode='OBJECT')
- bpy.ops.object.mode_set(mode='OBJECT')
-
- # Component statistics
- n_verts = len(me1.vertices)
-
- # Create empty lists
- new_verts = []
- new_edges = []
- new_faces = []
- new_verts_np = np.array(())
-
- # Component bounding box
- min_c = Vector((0, 0, 0))
- max_c = Vector((0, 0, 0))
- first = True
- for v in me1.vertices:
- vert = v.co
- if vert[0] < min_c[0] or first:
- min_c[0] = vert[0]
- if vert[1] < min_c[1] or first:
- min_c[1] = vert[1]
- if vert[2] < min_c[2] or first:
- min_c[2] = vert[2]
- if vert[0] > max_c[0] or first:
- max_c[0] = vert[0]
- if vert[1] > max_c[1] or first:
- max_c[1] = vert[1]
- if vert[2] > max_c[2] or first:
- max_c[2] = vert[2]
- first = False
- bb = max_c - min_c
-
- # adaptive XY
- verts1 = []
- for v in me1.vertices:
- if mode == 'BOUNDS':
- vert = v.co - min_c # (ob1.matrix_world * v.co) - min_c
- vert[0] = vert[0] / bb[0] if bb[0] != 0 else 0.5
- vert[1] = vert[1] / bb[1] if bb[1] != 0 else 0.5
- vert[2] = vert[2] / bb[2] if bb[2] != 0 else 0
- vert[2] = (vert[2] - 0.5 + offset * 0.5) * zscale
- elif mode == 'LOCAL':
- vert = v.co.xyz
- vert[2] *= zscale
- #vert[2] = (vert[2] - min_c[2] + (-0.5 + offset * 0.5) * bb[2]) * zscale
- elif mode == 'GLOBAL':
- vert = ob1.matrix_world @ v.co
- vert[2] *= zscale
- try:
- for sk in me1.shape_keys.key_blocks:
- sk.data[v.index].co = ob1.matrix_world @ sk.data[v.index].co
- except: pass
- #verts1.append(vert)
- v.co = vert
-
- # Bounds X, Y
- if mode != 'BOUNDS':
- if bounds_x == 'CYCLIC':
- move_verts = []
- for f in [f for f in me1.polygons if (f.center).x > 1]:
- for v in f.vertices:
- if v not in move_verts: move_verts.append(v)
- for v in move_verts:
- me1.vertices[v].co.x -= 1
- try:
- _ob1.active_shape_key_index = 0
- for sk in me1.shape_keys.key_blocks:
- sk.data[v].co.x -= 1
- except: pass
- move_verts = []
- for f in [f for f in me1.polygons if (f.center).x < 0]:
- for v in f.vertices:
- if v not in move_verts: move_verts.append(v)
- for v in move_verts:
- me1.vertices[v].co.x += 1
- try:
- _ob1.active_shape_key_index = 0
- for sk in me1.shape_keys.key_blocks:
- sk.data[v].co.x += 1
- except: pass
- if bounds_y == 'CYCLIC':
- move_verts = []
- for f in [f for f in me1.polygons if (f.center).y > 1]:
- for v in f.vertices:
- if v not in move_verts: move_verts.append(v)
- for v in move_verts:
- me1.vertices[v].co.y -= 1
- try:
- _ob1.active_shape_key_index = 0
- for sk in me1.shape_keys.key_blocks:
- sk.data[v].co.y -= 1
- except: pass
- move_verts = []
- for f in [f for f in me1.polygons if (f.center).y < 0]:
- for v in f.vertices:
- if v not in move_verts: move_verts.append(v)
- for v in move_verts:
- me1.vertices[v].co.y += 1
- try:
- _ob1.active_shape_key_index = 0
- for sk in me1.shape_keys.key_blocks:
- sk.data[v].co.y += 1
- except: pass
- verts1 = [v.co for v in me1.vertices]
- n_verts1 = len(verts1)
-
- patch_faces = 4**levels
- sides = int(sqrt(patch_faces))
- step = 1/sides
- sides0 = sides-2
- patch_faces0 = int((sides-2)**2)
- n_patches = int(len(me0.polygons)/patch_faces)
- if len(me0.polygons)%patch_faces != 0:
- #ob0.data = old_me0
- return "topology_error"
-
- new_verts = []
- new_edges = []
- new_faces = []
-
- for o in bpy.context.view_layer.objects: o.select_set(False)
- new_patch = None
-
- # All vertex group
- if bool_vertex_group:
- try:
- weight = []
- for vg in ob0.vertex_groups:
- _weight = []
- for v in me0.vertices:
- try:
- _weight.append(vg.weight(v.index))
- except:
- _weight.append(0)
- weight.append(_weight)
- except:
- bool_vertex_group = False
-
- # Adaptive Z
- if scale_mode == 'ADAPTIVE':
- com_area = bb[0]*bb[1]
- if mode != 'BOUNDS' or com_area == 0: com_area = 1
- #mult = 1/com_area
- verts_area = []
- bm = bmesh.new()
- bm.from_mesh(me0)
- bm.verts.ensure_lookup_table()
- for v in bm.verts:
- area = 0
- faces = v.link_faces
- for f in faces:
- area += f.calc_area()
- area = area/len(faces)*patch_faces/com_area
- #area*=mult*
- verts_area.append(sqrt(area)*bb[2])
-
- random.seed(rand_seed)
- bool_correct = False
-
- _faces = [[[0] for ii in range(sides)] for jj in range(sides)]
- _verts = [[[0] for ii in range(sides+1)] for jj in range(sides+1)]
-
- # find relative UV component's vertices
- verts1_uv_quads = [0]*len(verts1)
- verts1_uv = [0]*len(verts1)
- for i, vert in enumerate(verts1):
- # grid coordinates
- u = int(vert[0]//step)
- v = int(vert[1]//step)
- u1 = min(u+1, sides)
- v1 = min(v+1, sides)
- if mode != 'BOUNDS':
- if u > sides-1:
- u = sides-1
- u1 = sides
- if u < 0:
- u = 0
- u1 = 1
- if v > sides-1:
- v = sides-1
- v1 = sides
- if v < 0:
- v = 0
- v1 = 1
- verts1_uv_quads[i] = (u,v,u1,v1)
- # factor coordinates
- fu = (vert[0]-u*step)/step
- fv = (vert[1]-v*step)/step
- fw = vert.z
- # interpolate Z scaling factor
- verts1_uv[i] = Vector((fu,fv,fw))
-
- sk_uv_quads = []
- sk_uv = []
- if bool_shapekeys:
- for sk in ob1.data.shape_keys.key_blocks:
- source = sk.data
- _sk_uv_quads = [0]*len(verts1)
- _sk_uv = [0]*len(verts1)
- for i, sk_v in enumerate(source):
- if mode == 'BOUNDS':
- sk_vert = sk_v.co - min_c
- sk_vert[0] = (sk_vert[0] / bb[0] if bb[0] != 0 else 0.5)
- sk_vert[1] = (sk_vert[1] / bb[1] if bb[1] != 0 else 0.5)
- sk_vert[2] = (sk_vert[2] / bb[2] if bb[2] != 0 else sk_vert[2])
- sk_vert[2] = (sk_vert[2] - 0.5 + offset * 0.5) * zscale
- elif mode == 'LOCAL':
- sk_vert = sk_v.co
- sk_vert[2] *= zscale
- elif mode == 'GLOBAL':
- sk_vert = sk_v.co
- sk_vert[2] *= zscale
-
- # grid coordinates
- u = int(sk_vert[0]//step)
- v = int(sk_vert[1]//step)
- u1 = min(u+1, sides)
- v1 = min(v+1, sides)
- if mode != 'BOUNDS':
- if u > sides-1:
- u = sides-1
- u1 = sides
- if u < 0:
- u = 0
- u1 = 1
- if v > sides-1:
- v = sides-1
- v1 = sides
- if v < 0:
- v = 0
- v1 = 1
- _sk_uv_quads[i] = (u,v,u1,v1)
- # factor coordinates
- fu = (sk_vert[0]-u*step)/step
- fv = (sk_vert[1]-v*step)/step
- fw = sk_vert.z
- _sk_uv[i] = Vector((fu,fv,fw))
- sk_uv_quads.append(_sk_uv_quads)
- sk_uv.append(_sk_uv)
-
- for i in range(n_patches):
- poly = me0.polygons[i*patch_faces]
- if bool_selection and not poly.select: continue
- if bool_material_id and not poly.material_index == material_id: continue
-
- bool_correct = True
- new_patch = bpy.data.objects.new("patch", me1.copy())
- bpy.context.collection.objects.link(new_patch)
-
- new_patch.select_set(True)
- bpy.context.view_layer.objects.active = new_patch
-
- for area in bpy.context.screen.areas:
- for space in area.spaces:
- try: new_patch.local_view_set(space, True)
- except: pass
-
- # Vertex Group
- if bool_vertex_group:
- for vg in ob0.vertex_groups:
- new_patch.vertex_groups.new(name=vg.name)
-
- # find patch faces
- faces = _faces.copy()
- verts = _verts.copy()
- shift1 = sides
- shift2 = sides*2-1
- shift3 = sides*3-2
- for j in range(patch_faces):
- if j < patch_faces0:
- if levels == 0:
- u = j%sides0
- v = j//sides0
- else:
- u = j%sides0+1
- v = j//sides0+1
- elif j < patch_faces0 + shift1:
- u = j-patch_faces0
- v = 0
- elif j < patch_faces0 + shift2:
- u = sides-1
- v = j-(patch_faces0 + sides)+1
- elif j < patch_faces0 + shift3:
- jj = j-(patch_faces0 + shift2)
- u = sides-jj-2
- v = sides-1
- else:
- jj = j-(patch_faces0 + shift3)
- u = 0
- v = sides-jj-2
- face = me0.polygons[j+i*patch_faces]
- faces[u][v] = face
- verts[u][v] = verts0[face.vertices[0]]
- if u == sides-1:
- verts[sides][v] = verts0[face.vertices[1]]
- if v == sides-1:
- verts[u][sides] = verts0[face.vertices[3]]
- if u == v == sides-1:
- verts[sides][sides] = verts0[face.vertices[2]]
-
- # Random rotation
- if rotation_mode == 'RANDOM' or rotation_shift != 0:
- if rotation_mode == 'RANDOM': rot = random.randint(0, 3)
- else: rot = rotation_shift%4
- if rot == 1:
- verts = [[verts[w][k] for w in range(sides+1)] for k in range(sides,-1,-1)]
- elif rot == 2:
- verts = [[verts[k][w] for w in range(sides,-1,-1)] for k in range(sides,-1,-1)]
- elif rot == 3:
- verts = [[verts[w][k] for w in range(sides,-1,-1)] for k in range(sides+1)]
-
- # UV rotation
- if rotation_mode == 'UV' and ob0.type == 'MESH':
- if len(ob0.data.uv_layers) > 0:
- uv0 = me0.uv_layers.active.data[faces[0][0].index*4].uv
- uv1 = me0.uv_layers.active.data[faces[0][-1].index*4 + 3].uv
- uv2 = me0.uv_layers.active.data[faces[-1][-1].index*4 + 2].uv
- uv3 = me0.uv_layers.active.data[faces[-1][0].index*4 + 1].uv
- v01 = (uv0 + uv1)
- v32 = (uv3 + uv2)
- v0132 = v32 - v01
- v0132.normalize()
- v12 = (uv1 + uv2)
- v03 = (uv0 + uv3)
- v1203 = v03 - v12
- v1203.normalize()
-
- vertUV = []
- dot1203 = v1203.x
- dot0132 = v0132.x
- if(abs(dot1203) < abs(dot0132)):
- if (dot0132 > 0):
- pass
- else:
- verts = [[verts[k][w] for w in range(sides,-1,-1)] for k in range(sides,-1,-1)]
- else:
- if(dot1203 < 0):
- verts = [[verts[w][k] for w in range(sides,-1,-1)] for k in range(sides+1)]
- else:
- verts = [[verts[w][k] for w in range(sides+1)] for k in range(sides,-1,-1)]
-
- if True:
- verts_xyz = np.array([[v.co for v in _verts] for _verts in verts])
- #verts_norm = np.array([[v.normal for v in _verts] for _verts in verts])
- verts_norm = np.array([[normals0[v.index] for v in _verts] for _verts in verts])
- if normals_mode == 'FACES':
- verts_norm = np.mean(verts_norm, axis=(0,1))
- verts_norm = np.expand_dims(verts_norm, axis=0)
- verts_norm = np.repeat(verts_norm,len(verts),axis=0)
- verts_norm = np.expand_dims(verts_norm, axis=0)
- verts_norm = np.repeat(verts_norm,len(verts),axis=0)
- np_verts1_uv = np.array(verts1_uv)
- verts1_uv_quads = np.array(verts1_uv_quads)
- u = verts1_uv_quads[:,0]
- v = verts1_uv_quads[:,1]
- u1 = verts1_uv_quads[:,2]
- v1 = verts1_uv_quads[:,3]
- v00 = verts_xyz[u,v]
- v10 = verts_xyz[u1,v]
- v01 = verts_xyz[u,v1]
- v11 = verts_xyz[u1,v1]
- n00 = verts_norm[u,v]
- n10 = verts_norm[u1,v]
- n01 = verts_norm[u,v1]
- n11 = verts_norm[u1,v1]
- vx = np_verts1_uv[:,0].reshape((n_verts1,1))
- vy = np_verts1_uv[:,1].reshape((n_verts1,1))
- vz = np_verts1_uv[:,2].reshape((n_verts1,1))
- co2 = np_lerp2(v00,v10,v01,v11,vx,vy)
- n2 = np_lerp2(n00,n10,n01,n11,vx,vy)
- if scale_mode == 'ADAPTIVE':
- areas = np.array([[verts_area[v.index] for v in verts_v] for verts_v in verts])
- a00 = areas[u,v].reshape((n_verts1,1))
- a10 = areas[u1,v].reshape((n_verts1,1))
- a01 = areas[u,v1].reshape((n_verts1,1))
- a11 = areas[u1,v1].reshape((n_verts1,1))
- # remapped z scale
- a2 = np_lerp2(a00,a10,a01,a11,vx,vy)
- co3 = co2 + n2 * vz * a2
- else:
- co3 = co2 + n2 * vz
- coordinates = co3.flatten().tolist()
- new_patch.data.vertices.foreach_set('co',coordinates)
-
- # vertex groups
- if bool_vertex_group:
- for _weight, vg in zip(weight, new_patch.vertex_groups):
- np_weight = np.array([[_weight[v.index] for v in verts_v] for verts_v in verts])
- w00 = np_weight[u,v].reshape((n_verts1,1))
- w10 = np_weight[u1,v].reshape((n_verts1,1))
- w01 = np_weight[u,v1].reshape((n_verts1,1))
- w11 = np_weight[u1,v1].reshape((n_verts1,1))
- # remapped z scale
- w2 = np_lerp2(w00,w10,w01,w11,vx,vy)
- for vert_id in range(n_verts1):
- vg.add([vert_id], w2[vert_id], "ADD")
-
- if bool_shapekeys:
- for i_sk, sk in enumerate(ob1.data.shape_keys.key_blocks):
- np_verts1_uv = np.array(sk_uv[i_sk])
- np_sk_uv_quads = np.array(sk_uv_quads[i_sk])
- u = np_sk_uv_quads[:,0]
- v = np_sk_uv_quads[:,1]
- u1 = np_sk_uv_quads[:,2]
- v1 = np_sk_uv_quads[:,3]
- v00 = verts_xyz[u,v]
- v10 = verts_xyz[u1,v]
- v01 = verts_xyz[u,v1]
- v11 = verts_xyz[u1,v1]
- vx = np_verts1_uv[:,0].reshape((n_verts1,1))
- vy = np_verts1_uv[:,1].reshape((n_verts1,1))
- vz = np_verts1_uv[:,2].reshape((n_verts1,1))
- co2 = np_lerp2(v00,v10,v01,v11,vx,vy)
- n2 = np_lerp2(n00,n10,n01,n11,vx,vy)
- if scale_mode == 'ADAPTIVE':
- areas = np.array([[verts_area[v.index] for v in verts_v] for verts_v in verts])
- a00 = areas[u,v].reshape((n_verts1,1))
- a10 = areas[u1,v].reshape((n_verts1,1))
- a01 = areas[u,v1].reshape((n_verts1,1))
- a11 = areas[u1,v1].reshape((n_verts1,1))
- # remapped z scale
- a2 = np_lerp2(a00,a10,a01,a11,vx,vy)
- co3 = co2 + n2 * vz * a2
- else:
- co3 = co2 + n2 * vz
- coordinates = co3.flatten().tolist()
- new_patch.data.shape_keys.key_blocks[sk.name].data.foreach_set('co', coordinates)
- #new_patch.data.shape_keys.key_blocks[sk.name].data[i_vert].co = sk_co
- else:
- for _fvec, uv_quad, patch_vert in zip(verts1_uv, verts1_uv_quads, new_patch.data.vertices):
- u = uv_quad[0]
- v = uv_quad[1]
- u1 = uv_quad[2]
- v1 = uv_quad[3]
- v00 = verts[u][v]
- v10 = verts[u1][v]
- v01 = verts[u][v1]
- v11 = verts[u1][v1]
- # interpolate Z scaling factor
- fvec = _fvec.copy()
- if scale_mode == 'ADAPTIVE':
- a00 = verts_area[v00.index]
- a10 = verts_area[v10.index]
- a01 = verts_area[v01.index]
- a11 = verts_area[v11.index]
- fvec[2]*=lerp2(a00,a10,a01,a11,fvec)
- # interpolate vertex on patch
- patch_vert.co = lerp3(v00, v10, v01, v11, fvec)
-
- # Vertex Group
- if bool_vertex_group:
- for _weight, vg in zip(weight, new_patch.vertex_groups):
- w00 = _weight[v00.index]
- w10 = _weight[v10.index]
- w01 = _weight[v01.index]
- w11 = _weight[v11.index]
- wuv = lerp2(w00,w10,w01,w11, fvec)
- vg.add([patch_vert.index], wuv, "ADD")
-
- if bool_shapekeys:
- for i_sk, sk in enumerate(ob1.data.shape_keys.key_blocks):
- for i_vert, _fvec, _sk_uv_quad in zip(range(len(new_patch.data.vertices)), sk_uv[i_sk], sk_uv_quads[i_sk]):
- u = _sk_uv_quad[0]
- v = _sk_uv_quad[1]
- u1 = _sk_uv_quad[2]
- v1 = _sk_uv_quad[3]
- v00 = verts[u][v]
- v10 = verts[u1][v]
- v01 = verts[u][v1]
- v11 = verts[u1][v1]
-
- fvec = _fvec.copy()
- if scale_mode == 'ADAPTIVE':
- a00 = verts_area[v00.index]
- a10 = verts_area[v10.index]
- a01 = verts_area[v01.index]
- a11 = verts_area[v11.index]
- fvec[2]*=lerp2(a00, a10, a01, a11, fvec)
- sk_co = lerp3(v00, v10, v01, v11, fvec)
-
- new_patch.data.shape_keys.key_blocks[sk.name].data[i_vert].co = sk_co
-
- #if ob0.type == 'MESH': ob0.data = old_me0
- if not bool_correct: return 0
-
- bpy.ops.object.join()
-
-
- if bool_shapekeys:
- # set original values and combine Shape Keys and Vertex Groups
- for sk, val in zip(_ob1.data.shape_keys.key_blocks, original_key_values):
- sk.value = val
- new_patch.data.shape_keys.key_blocks[sk.name].value = val
- if bool_vertex_group:
- for sk in new_patch.data.shape_keys.key_blocks:
- for vg in new_patch.vertex_groups:
- if sk.name == vg.name:
- sk.vertex_group = vg.name
- else:
- try:
- for sk, val in zip(_ob1.data.shape_keys.key_blocks, original_key_values):
- sk.value = val
- except: pass
-
- new_name = ob0.name + "_" + ob1.name
- new_patch.name = "tessellate_temp"
-
- if bool_multires:
- for m in ob0.modifiers:
- if m.type == 'MULTIRES' and m.name == multires_name:
- m.levels = levels
- m.sculpt_levels = sculpt_levels
- m.render_levels = render_levels
- # restore original modifiers visibility for component object
- try:
- for m, vis in zip(_ob1.modifiers, mod_visibility):
- m.show_viewport = vis
- except: pass
-
- bpy.data.objects.remove(before)
- bpy.data.objects.remove(ob0)
- bpy.data.objects.remove(ob1)
- return new_patch
-
- def tessellate_original(_ob0, _ob1, offset, zscale, gen_modifiers, com_modifiers, mode,
- scale_mode, rotation_mode, rotation_shift, rotation_direction, rand_seed, fill_mode,
- bool_vertex_group, bool_selection, bool_shapekeys,
- bool_material_id, material_id, normals_mode, bounds_x, bounds_y):
-
- if com_modifiers or _ob1.type != 'MESH': bool_shapekeys = False
- random.seed(rand_seed)
-
- if bool_shapekeys:
- try:
- original_key_values = []
- for sk in _ob1.data.shape_keys.key_blocks:
- original_key_values.append(sk.value)
- sk.value = 0
- except:
- bool_shapekeys = False
-
- if normals_mode == 'CUSTOM':
- if _ob0.data.shape_keys != None:
- ob0_sk = convert_object_to_mesh(_ob0, True, True)
- me0_sk = ob0_sk.data
- key_values0 = [sk.value for sk in _ob0.data.shape_keys.key_blocks]
- for sk in _ob0.data.shape_keys.key_blocks: sk.value = 0
- else: normals_mode == 'VERTS'
-
- ob0 = convert_object_to_mesh(_ob0, gen_modifiers, True)
- me0 = ob0.data
- ob1 = convert_object_to_mesh(_ob1, com_modifiers, True)
- me1 = ob1.data
-
- # base normals
- normals0 = []
- if normals_mode == 'CUSTOM' and _ob0.data.shape_keys != None:
- for sk, val in zip(_ob0.data.shape_keys.key_blocks, key_values0): sk.value = val
- for v0, v1 in zip(me0.vertices, me0_sk.vertices):
- normals0.append(v1.co - v0.co)
- bpy.data.objects.remove(ob0_sk)
- else:
- me0.update()
- normals0 = [v.normal for v in me0.vertices]
-
- base_polygons = []
- base_face_normals = []
-
- n_faces0 = len(me0.polygons)
-
- # Check if zero faces are selected
- if (bool_selection and ob0.type == 'MESH') or bool_material_id:
- for p in me0.polygons:
- if (bool_selection and ob0.type == 'MESH'):
- is_sel = p.select
- else: is_sel = True
- if bool_material_id:
- is_mat = p.material_index == material_id
- else: is_mat = True
- if is_sel and is_mat:
- base_polygons.append(p)
- base_face_normals.append(p.normal)
- else:
- base_polygons = me0.polygons
- base_face_normals = [p.normal for p in me0.polygons]
-
- # numpy test: slower
- #base_face_normals = np.zeros(n_faces0*3)
- #me0.polygons.foreach_get("normal", base_face_normals)
- #base_face_normals = base_face_normals.reshape((n_faces0,3))
-
- if len(base_polygons) == 0:
- bpy.data.objects.remove(ob0)
- bpy.data.objects.remove(ob1)
- bpy.data.meshes.remove(me1)
- bpy.data.meshes.remove(me0)
- return 0
-
- if mode != 'BOUNDS':
-
- bpy.ops.object.select_all(action='DESELECT')
- for o in bpy.context.view_layer.objects: o.select_set(False)
- bpy.context.view_layer.objects.active = ob1
- ob1.select_set(True)
- ob1.active_shape_key_index = 0
- # Bound X
- if bounds_x != 'EXTEND':
- if mode == 'GLOBAL':
- planes_co = ((0,0,0),(1,1,1))
- plane_no = (1,0,0)
- if mode == 'LOCAL':
- planes_co = (ob1.matrix_world @ Vector((0,0,0)), ob1.matrix_world @ Vector((1,0,0)))
- plane_no = planes_co[0]-planes_co[1]
- bpy.ops.object.mode_set(mode='EDIT')
- for co in planes_co:
- bpy.ops.mesh.select_all(action='SELECT')
- bpy.ops.mesh.bisect(plane_co=co, plane_no=plane_no)
- bpy.ops.mesh.mark_seam()
- bpy.ops.object.mode_set(mode='OBJECT')
- _faces = ob1.data.polygons
- if mode == 'GLOBAL':
- for f in [f for f in _faces if (ob1.matrix_world @ f.center).x > 1]:
- f.select = True
- for f in [f for f in _faces if (ob1.matrix_world @ f.center).x < 0]:
- f.select = True
- else:
- for f in [f for f in _faces if f.center.x > 1]:
- f.select = True
- for f in [f for f in _faces if f.center.x < 0]:
- f.select = True
- bpy.ops.object.mode_set(mode='EDIT')
- bpy.ops.mesh.select_mode(type='FACE')
- if bounds_x == 'CLIP':
- bpy.ops.mesh.delete(type='FACE')
- bpy.ops.object.mode_set(mode='OBJECT')
- if bounds_x == 'CYCLIC':
- bpy.ops.mesh.split()
- bpy.ops.object.mode_set(mode='OBJECT')
- # Bound Y
- if bounds_y != 'EXTEND':
- if mode == 'GLOBAL':
- planes_co = ((0,0,0),(1,1,1))
- plane_no = (0,1,0)
- if mode == 'LOCAL':
- planes_co = (ob1.matrix_world @ Vector((0,0,0)), ob1.matrix_world @ Vector((0,1,0)))
- plane_no = planes_co[0]-planes_co[1]
- bpy.ops.object.mode_set(mode='EDIT')
- for co in planes_co:
- bpy.ops.mesh.select_all(action='SELECT')
- bpy.ops.mesh.bisect(plane_co=co, plane_no=plane_no)
- bpy.ops.mesh.mark_seam()
- bpy.ops.object.mode_set(mode='OBJECT')
- _faces = ob1.data.polygons
- if mode == 'GLOBAL':
- for f in [f for f in _faces if (ob1.matrix_world @ f.center).y > 1]:
- f.select = True
- for f in [f for f in _faces if (ob1.matrix_world @ f.center).y < 0]:
- f.select = True
- else:
- for f in [f for f in _faces if f.center.y > 1]:
- f.select = True
- for f in [f for f in _faces if f.center.y < 0]:
- f.select = True
-
- bpy.ops.object.mode_set(mode='EDIT')
- bpy.ops.mesh.select_mode(type='FACE')
- if bounds_y == 'CLIP':
- bpy.ops.mesh.delete(type='FACE')
- bpy.ops.object.mode_set(mode='OBJECT')
- if bounds_y == 'CYCLIC':
- bpy.ops.mesh.split()
- bpy.ops.object.mode_set(mode='OBJECT')
- bpy.ops.object.mode_set(mode='OBJECT')
- #ob1 = new_ob1
-
- me1 = ob1.data
-
- verts0 = me0.vertices # Collect generator vertices
-
- # Component statistics
- n_verts1 = len(me1.vertices)
- n_edges1 = len(me1.edges)
- n_faces1 = len(me1.polygons)
-
- # Create empty lists
- new_verts = []
- new_edges = []
- new_faces = []
- new_verts_np = np.array(())
-
- # Component Coordinates
- co1 = [0]*n_verts1*3
-
- if mode == 'GLOBAL':
- for v in me1.vertices:
- v.co = ob1.matrix_world @ v.co
- try:
- for sk in me1.shape_keys.key_blocks:
- sk.data[v.index].co = ob1.matrix_world @ sk.data[v.index].co
- except: pass
- if mode != 'BOUNDS':
- if bounds_x == 'CYCLIC':
- move_verts = []
- for f in [f for f in me1.polygons if (f.center).x > 1]:
- for v in f.vertices:
- if v not in move_verts: move_verts.append(v)
- for v in move_verts:
- me1.vertices[v].co.x -= 1
- try:
- _ob1.active_shape_key_index = 0
- for sk in me1.shape_keys.key_blocks:
- sk.data[v].co.x -= 1
- except: pass
- move_verts = []
- for f in [f for f in me1.polygons if (f.center).x < 0]:
- for v in f.vertices:
- if v not in move_verts: move_verts.append(v)
- for v in move_verts:
- me1.vertices[v].co.x += 1
- try:
- _ob1.active_shape_key_index = 0
- for sk in me1.shape_keys.key_blocks:
- sk.data[v].co.x += 1
- except: pass
- if bounds_y == 'CYCLIC':
- move_verts = []
- for f in [f for f in me1.polygons if (f.center).y > 1]:
- for v in f.vertices:
- if v not in move_verts: move_verts.append(v)
- for v in move_verts:
- me1.vertices[v].co.y -= 1
- try:
- #new_ob1.active_shape_key_index = 0
- for sk in me1.shape_keys.key_blocks:
- sk.data[v].co.y -= 1
- except: pass
- move_verts = []
- for f in [f for f in me1.polygons if (f.center).y < 0]:
- for v in f.vertices:
- if v not in move_verts: move_verts.append(v)
- for v in move_verts:
- me1.vertices[v].co.y += 1
- try:
- #new_ob1.active_shape_key_index = 0
- for sk in me1.shape_keys.key_blocks:
- sk.data[v].co.y += 1
- except: pass
- if len(me1.vertices) == 0:
- bpy.data.objects.remove(ob0)
- bpy.data.objects.remove(ob1)
- return 0
-
- me1.vertices.foreach_get("co", co1)
- co1 = np.array(co1)
- vx = co1[0::3].reshape((n_verts1,1))
- vy = co1[1::3].reshape((n_verts1,1))
- vz = co1[2::3].reshape((n_verts1,1))
- min_c = Vector((vx.min(), vy.min(), vz.min())) # Min BB Corner
- max_c = Vector((vx.max(), vy.max(), vz.max())) # Max BB Corner
- bb = max_c - min_c # Bounding Box
-
- # Component Coordinates
- if mode == 'BOUNDS':
- vx = (vx - min_c[0]) / bb[0] if bb[0] != 0 else 0.5
- vy = (vy - min_c[1]) / bb[1] if bb[1] != 0 else 0.5
- vz = (vz - min_c[2]) / bb[2] if bb[2] != 0 else 0
- vz = (vz - 0.5 + offset * 0.5) * zscale
- #vz = ((vz - min_c[2]) + (-0.5 + offset * 0.5) * bb[2]) * zscale
- else:
- vz *= zscale
-
- # Component polygons
- fs1 = [[i for i in p.vertices] for p in me1.polygons]
- new_faces = fs1[:]
-
- # Component edges
- es1 = np.array([[i for i in e.vertices] for e in me1.edges])
- #es1 = [[i for i in e.vertices] for e in me1.edges if e.is_loose]
- new_edges = es1[:]
-
- # SHAPE KEYS
- if bool_shapekeys:
- basis = True #com_modifiers
- vx_key = []
- vy_key = []
- vz_key = []
- sk_np = []
- for sk in ob1.data.shape_keys.key_blocks:
- do_shapekeys = True
- # set all keys to 0
- for _sk in ob1.data.shape_keys.key_blocks: _sk.value = 0
- sk.value = 1
-
- if basis:
- basis = False
- continue
-
- # Apply component modifiers
- if com_modifiers:
- sk_ob = convert_object_to_mesh(_ob1)
- sk_data = sk_ob.data
- source = sk_data.vertices
- else:
- source = sk.data
-
- shapekeys = []
- for v in source:
- if mode == 'BOUNDS':
- vert = v.co - min_c
- vert[0] = (vert[0] / bb[0] if bb[0] != 0 else 0.5)
- vert[1] = (vert[1] / bb[1] if bb[1] != 0 else 0.5)
- vert[2] = (vert[2] / bb[2] if bb[2] != 0 else vert[2])
- vert[2] = (vert[2] - 0.5 + offset * 0.5) * zscale
- elif mode == 'LOCAL':
- vert = v.co.xyz
- vert[2] *= zscale
- #vert[2] = (vert[2] - min_c[2] + (-0.5 + offset * 0.5) * bb[2]) * \
- # zscale
- elif mode == 'GLOBAL':
- vert = v.co.xyz
- #vert = ob1.matrix_world @ v.co
- vert[2] *= zscale
- shapekeys.append(vert)
-
- # Component vertices
- key1 = np.array([v for v in shapekeys]).reshape(len(shapekeys), 3, 1)
- vx_key.append(key1[:, 0])
- vy_key.append(key1[:, 1])
- vz_key.append(key1[:, 2])
- #sk_np.append([])
-
- # All vertex group
- if bool_vertex_group or rotation_mode == 'WEIGHT':
- try:
- weight = []
- for vg in ob0.vertex_groups:
- _weight = []
- for i,v in enumerate(me0.vertices):
- try:
- _weight.append(vg.weight(i))
- except:
- _weight.append(0)
- weight.append(_weight)
- except:
- bool_vertex_group = False
-
- # Adaptive Z
- if scale_mode == 'ADAPTIVE':
- com_area = bb[0]*bb[1]
- if mode != 'BOUNDS' or com_area == 0: com_area = 1
- verts_area = []
- bm = bmesh.new()
- bm.from_mesh(me0)
- bm.verts.ensure_lookup_table()
- for v in bm.verts:
- area = 0
- faces = v.link_faces
- for f in faces:
- area += f.calc_area()
- try:
- area/=len(faces) # average area
- area/=com_area
- verts_area.append(sqrt(area)*bb[2])
- #verts_area.append(area)
- except:
- verts_area.append(1)
-
- count = 0 # necessary for UV calculation
-
- # TESSELLATION
- j = 0
- jj = -1
- bool_correct = False
-
- # optimization test
- n_faces = len(base_polygons)
- _vs0 = [0]*n_faces
- _nvs0 = [0]*n_faces
- _sz = [0]*n_faces
- n_vg = len(ob0.vertex_groups)
- _w0 = [[0]*n_faces for i in range(n_vg)]
- np_faces = [np.array(p) for p in fs1]
- new_faces = [0]*n_faces*n_faces1
- face1_count = 0
-
- for j, p in enumerate(base_polygons):
-
- bool_correct = True
- if rotation_mode in ['UV', 'WEIGHT'] and ob0.type != 'MESH':
- rotation_mode = 'DEFAULT'
-
- ordered = p.vertices
-
- # Random rotation
- if rotation_mode == 'RANDOM':
- shifted_vertices = []
- n_poly_verts = len(p.vertices)
- rand = random.randint(0, n_poly_verts)
- for i in range(n_poly_verts):
- shifted_vertices.append(p.vertices[(i + rand) % n_poly_verts])
- if scale_mode == 'ADAPTIVE':
- verts_area0 = np.array([verts_area[i] for i in shifted_vertices])
- ordered = shifted_vertices
-
- # UV rotation
- elif rotation_mode == 'UV':
- if len(ob0.data.uv_layers) > 0:
- i = p.index
- if bool_material_id:
- count = sum([len(p.vertices) for p in me0.polygons[:i]])
- #if i == 0: count = 0
- v01 = (me0.uv_layers.active.data[count].uv +
- me0.uv_layers.active.data[count + 1].uv)
- if len(p.vertices) > 3:
- v32 = (me0.uv_layers.active.data[count + 3].uv +
- me0.uv_layers.active.data[count + 2].uv)
- else:
- v32 = (me0.uv_layers.active.data[count].uv +
- me0.uv_layers.active.data[count + 2].uv)
- v0132 = v32 - v01
- v0132.normalize()
-
- v12 = (me0.uv_layers.active.data[count + 1].uv +
- me0.uv_layers.active.data[count + 2].uv)
- if len(p.vertices) > 3:
- v03 = (me0.uv_layers.active.data[count].uv +
- me0.uv_layers.active.data[count + 3].uv)
- else:
- v03 = (me0.uv_layers.active.data[count].uv +
- me0.uv_layers.active.data[count].uv)
- v1203 = v03 - v12
- v1203.normalize()
-
- vertUV = []
- dot1203 = v1203.x
- dot0132 = v0132.x
- if(abs(dot1203) < abs(dot0132)):
- if (dot0132 > 0):
- vertUV = p.vertices[1:] + p.vertices[:1]
- else:
- vertUV = p.vertices[3:] + p.vertices[:3]
- else:
- if(dot1203 < 0):
- vertUV = p.vertices[:]
- else:
- vertUV = p.vertices[2:] + p.vertices[:2]
- ordered = vertUV
- count += len(p.vertices)
-
- # Weight Rotation
- elif rotation_mode == 'WEIGHT':
- if len(weight) > 0:
- active_weight = weight[ob0.vertex_groups.active_index]
- i = p.index
- face_weights = [active_weight[v] for v in p.vertices]
- face_weights*=2
- if rotation_direction == 'DIAG':
- differential = [face_weights[ii]-face_weights[ii+2] for ii in range(4)]
- else:
- differential = [face_weights[ii]+face_weights[ii+1]-face_weights[ii+2]- face_weights[ii+3] for ii in range(4)]
- starting = differential.index(max(differential))
-
- ordered = p.vertices[starting:] + p.vertices[:starting]
-
- if rotation_mode != 'RANDOM':
- ordered = np.roll(np.array(ordered),rotation_shift)
- ordered = np.array((ordered[0], ordered[1], ordered[2], ordered[-1]))
-
- # assign vertices and values
- vs0 = np.array([verts0[i].co for i in ordered])
- #nvs0 = np.array([verts0[i].normal for i in ordered])
- nvs0 = np.array([normals0[i] for i in ordered])
- if scale_mode == 'ADAPTIVE':
- np_verts_area = np.array([verts_area[i] for i in ordered])
- _sz[j] = np_verts_area
- # Vertex weight
- if bool_vertex_group:
- ws0 = []
- for w in weight:
- _ws0 = []
- for i in ordered:
- try:
- _ws0.append(w[i])
- except:
- _ws0.append(0)
- ws0.append(np.array(_ws0))
-
- # optimization test
- _vs0[j] = (vs0[0], vs0[1], vs0[2], vs0[-1])
- if normals_mode != 'FACES':
- _nvs0[j] = (nvs0[0], nvs0[1], nvs0[2], nvs0[-1])
-
- if bool_vertex_group:
- for i_vg, ws0_face in enumerate(ws0):
- _w0[i_vg][j] = (ws0_face[0], ws0_face[1], ws0_face[2], ws0_face[-1])
-
- for p in fs1:
- new_faces[face1_count] = [i + n_verts1 * j for i in p]
- face1_count += 1
-
- # build edges list
- n_edges1 = new_edges.shape[0]
- new_edges = new_edges.reshape((1, n_edges1, 2))
- new_edges = new_edges.repeat(n_faces,axis=0)
- new_edges = new_edges.reshape((n_edges1*n_faces, 2))
- increment = np.arange(n_faces)*n_verts1
- increment = increment.repeat(n_edges1, axis=0)
- increment = increment.reshape((n_faces*n_edges1,1))
- new_edges = new_edges + increment
-
- # optimization test
- _vs0 = np.array(_vs0)
- _sz = np.array(_sz)
-
- _vs0_0 = _vs0[:,0].reshape((n_faces,1,3))
- _vs0_1 = _vs0[:,1].reshape((n_faces,1,3))
- _vs0_2 = _vs0[:,2].reshape((n_faces,1,3))
- _vs0_3 = _vs0[:,3].reshape((n_faces,1,3))
-
- # remapped vertex coordinates
- v2 = np_lerp2(_vs0_0, _vs0_1, _vs0_3, _vs0_2, vx, vy)
-
- # remapped vertex normal
- if normals_mode != 'FACES':
- _nvs0 = np.array(_nvs0)
- _nvs0_0 = _nvs0[:,0].reshape((n_faces,1,3))
- _nvs0_1 = _nvs0[:,1].reshape((n_faces,1,3))
- _nvs0_2 = _nvs0[:,2].reshape((n_faces,1,3))
- _nvs0_3 = _nvs0[:,3].reshape((n_faces,1,3))
- nv2 = np_lerp2(_nvs0_0, _nvs0_1, _nvs0_3, _nvs0_2, vx, vy)
- else:
- nv2 = np.array(base_face_normals).reshape((n_faces,1,3))
-
- # interpolate vertex groups
- if bool_vertex_group:
- w = np.array(_w0)
- w_0 = w[:,:,0].reshape((n_vg, n_faces,1,1))
- w_1 = w[:,:,1].reshape((n_vg, n_faces,1,1))
- w_2 = w[:,:,2].reshape((n_vg, n_faces,1,1))
- w_3 = w[:,:,3].reshape((n_vg, n_faces,1,1))
- # remapped weight
- w = np_lerp2(w_0, w_1, w_3, w_2, vx, vy)
- w = w.reshape((n_vg, n_faces*n_verts1))
-
- if scale_mode == 'ADAPTIVE':
- _sz_0 = _sz[:,0].reshape((n_faces,1,1))
- _sz_1 = _sz[:,1].reshape((n_faces,1,1))
- _sz_2 = _sz[:,2].reshape((n_faces,1,1))
- _sz_3 = _sz[:,3].reshape((n_faces,1,1))
- # remapped z scale
- sz2 = np_lerp2(_sz_0, _sz_1, _sz_3, _sz_2, vx, vy)
- v3 = v2 + nv2 * vz * sz2
- else:
- v3 = v2 + nv2 * vz
-
- new_verts_np = v3.reshape((n_faces*n_verts1,3))
-
- if bool_shapekeys:
- n_sk = len(vx_key)
- sk_np = [0]*n_sk
- for i in range(n_sk):
- vx = np.array(vx_key[i])
- vy = np.array(vy_key[i])
- vz = np.array(vz_key[i])
-
- # remapped vertex coordinates
- v2 = np_lerp2(_vs0_0, _vs0_1, _vs0_3, _vs0_2, vx, vy)
-
- # remapped vertex normal
- if normals_mode != 'FACES':
- nv2 = np_lerp2(_nvs0_0, _nvs0_1, _nvs0_3, _nvs0_2, vx, vy)
- else:
- nv2 = np.array(base_face_normals).reshape((n_faces,1,3))
-
- if scale_mode == 'ADAPTIVE':
- # remapped z scale
- sz2 = np_lerp2(_sz_0, _sz_1, _sz_3, _sz_2, vx, vy)
- v3 = v2 + nv2 * vz * sz2
- else:
- v3 = v2 + nv2 * vz
-
- sk_np[i] = v3.reshape((n_faces*n_verts1,3))
-
- #if ob0.type == 'MESH': ob0.data = old_me0
-
- if not bool_correct:
- #bpy.data.objects.remove(ob1)
- return 0
-
- new_verts = new_verts_np.tolist()
- new_name = ob0.name + "_" + ob1.name
- new_me = bpy.data.meshes.new(new_name)
- new_me.from_pydata(new_verts, new_edges.tolist(), new_faces)
- new_me.update(calc_edges=True)
- new_ob = bpy.data.objects.new("tessellate_temp", new_me)
-
- # vertex group
- if bool_vertex_group and False:
- for vg in ob0.vertex_groups:
- new_ob.vertex_groups.new(name=vg.name)
- for i in range(len(vg_np[vg.index])):
- new_ob.vertex_groups[vg.name].add([i], vg_np[vg.index][i],"ADD")
- # vertex group
- if bool_vertex_group:
- for vg in ob0.vertex_groups:
- new_ob.vertex_groups.new(name=vg.name)
- for i, vertex_weight in enumerate(w[vg.index]):
- new_ob.vertex_groups[vg.name].add([i], vertex_weight,"ADD")
-
- if bool_shapekeys:
- basis = com_modifiers
- sk_count = 0
- for sk, val in zip(_ob1.data.shape_keys.key_blocks, original_key_values):
- sk.value = val
- new_ob.shape_key_add(name=sk.name, from_mix=False)
- new_ob.data.shape_keys.key_blocks[sk.name].value = val
- # set shape keys vertices
- sk_data = new_ob.data.shape_keys.key_blocks[sk.name].data
- if sk_count == 0:
- sk_count += 1
- continue
- for id in range(len(sk_data)):
- sk_data[id].co = sk_np[sk_count-1][id]
- sk_count += 1
- if bool_vertex_group:
- for sk in new_ob.data.shape_keys.key_blocks:
- for vg in new_ob.vertex_groups:
- if sk.name == vg.name:
- sk.vertex_group = vg.name
-
- # EDGES SEAMS
- edge_data = [0]*n_edges1
- me1.edges.foreach_get("use_seam",edge_data)
- if any(edge_data):
- edge_data = edge_data*n_faces
- new_ob.data.edges.foreach_set("use_seam",edge_data)
-
- # EDGES SHARP
- edge_data = [0]*n_edges1
- me1.edges.foreach_get("use_edge_sharp",edge_data)
- if any(edge_data):
- edge_data = edge_data*n_faces
- new_ob.data.edges.foreach_set("use_edge_sharp",edge_data)
-
- bpy.ops.object.select_all(action='DESELECT')
- bpy.context.collection.objects.link(new_ob)
- new_ob.select_set(True)
- bpy.context.view_layer.objects.active = new_ob
-
- # EDGES BEVEL
- edge_data = [0]*n_edges1
- me1.edges.foreach_get("bevel_weight",edge_data)
- if any(edge_data):
- bpy.ops.object.mode_set(mode='EDIT')
- bpy.ops.mesh.select_all(action='SELECT')
- bpy.ops.transform.edge_bevelweight(value=1)
- bpy.ops.object.mode_set(mode='OBJECT')
- edge_data = edge_data*n_faces
- new_ob.data.edges.foreach_set("bevel_weight",edge_data)
-
- # EDGE CREASES
- edge_data = [0]*n_edges1
- me1.edges.foreach_get("crease",edge_data)
- if any(edge_data):
- bpy.ops.object.mode_set(mode='EDIT')
- bpy.ops.mesh.select_all(action='SELECT')
- bpy.ops.transform.edge_crease(value=1)
- bpy.ops.object.mode_set(mode='OBJECT')
- edge_data = edge_data*n_faces
- new_ob.data.edges.foreach_set('crease', edge_data)
-
- # MATERIALS
- for slot in ob1.material_slots: new_ob.data.materials.append(slot.material)
-
- polygon_materials = [0]*n_faces1
- me1.polygons.foreach_get("material_index", polygon_materials)
- polygon_materials *= n_faces
- new_ob.data.polygons.foreach_set("material_index", polygon_materials)
- new_ob.data.update() ###
-
- try:
- bpy.data.objects.remove(new_ob1)
- except: pass
-
- bpy.data.objects.remove(ob0)
- bpy.data.meshes.remove(me0)
- bpy.data.objects.remove(ob1)
- bpy.data.meshes.remove(me1)
-
- return new_ob
-
-
- class tissue_tessellate(Operator):
- bl_idname = "object.tissue_tessellate"
- bl_label = "Tessellate"
- bl_description = ("Create a copy of selected object on the active object's "
- "faces, adapting the shape to the different faces")
- bl_options = {'REGISTER', 'UNDO'}
-
-
- bool_hold : BoolProperty(
- name="Hold",
- description="Wait...",
- default=False
- )
- bool_lock : BoolProperty(
- name="Lock",
- description="Prevent automatic update on settings changes or if other objects have it in the hierarchy.",
- default=False
- )
- bool_dependencies : BoolProperty(
- name="Update Dependencies",
- description="Automatically updates base and components as well, if results of other tessellations",
- default=False
- )
- object_name : StringProperty(
- name="",
- description="Name of the generated object"
- )
- zscale : FloatProperty(
- name="Scale",
- default=1,
- soft_min=0,
- soft_max=10,
- description="Scale factor for the component thickness"
- )
- scale_mode : EnumProperty(
- items=(
- ('CONSTANT', "Constant", "Uniform thickness"),
- ('ADAPTIVE', "Relative", "Preserve component's proportions")
- ),
- default='ADAPTIVE',
- name="Z-Scale according to faces size"
- )
- offset : FloatProperty(
- name="Surface Offset",
- default=1,
- min=-1, max=1,
- soft_min=-1,
- soft_max=1,
- description="Surface offset"
- )
- mode : EnumProperty(
- items=(
- ('BOUNDS', "Bounds", "The component fits automatically the size of the target face"),
- ('LOCAL', "Local", "Based on Local coordinates, from 0 to 1"),
- ('GLOBAL', 'Global', "Based on Global coordinates, from 0 to 1")),
- default='BOUNDS',
- name="Component Mode"
- )
- rotation_mode : EnumProperty(
- items=(('RANDOM', "Random", "Random faces rotation"),
- ('UV', "Active UV", "Face rotation is based on UV coordinates"),
- ('WEIGHT', "Active Weight", "Rotate according to Vertex Group gradient"),
- ('DEFAULT', "Default", "Default rotation")),
- default='DEFAULT',
- name="Component Rotation"
- )
- rotation_direction : EnumProperty(
- items=(('ORTHO', "Orthogonal", "Component main directions in XY"),
- ('DIAG', "Diagonal", "Component main direction aligned with diagonal")),
- default='ORTHO',
- name="Direction"
- )
- rotation_shift : IntProperty(
- name="Shift",
- default=0,
- soft_min=0,
- soft_max=3,
- description="Shift components rotation"
- )
- fill_mode : EnumProperty(
- items=(
- ('QUAD', 'Quad', 'Regular quad tessellation. Uses only 3 or 4 vertices'),
- ('FAN', 'Fan', 'Radial tessellation for polygonal faces'),
- ('PATCH', 'Patch', 'Curved tessellation according to the last ' +
- 'Subsurf\n(or Multires) modifiers. Works only with 4 sides ' +
- 'patches.\nAfter the last Subsurf (or Multires) only ' +
- 'deformation\nmodifiers can be used'),
- ('FRAME', 'Frame', 'Essellation along the edges of each face')),
- default='QUAD',
- name="Fill Mode"
- )
- combine_mode : EnumProperty(
- items=(
- ('LAST', 'Last', 'Show only the last iteration'),
- ('UNUSED', 'Unused', 'Combine each iteration with the unused faces of the previous iteration. Used for branching systems'),
- ('ALL', 'All', 'Combine the result of all iterations')),
- default='LAST',
- name="Combine Mode",
- )
- gen_modifiers : BoolProperty(
- name="Generator Modifiers",
- default=False,
- description="Apply Modifiers and Shape Keys to the base object"
- )
- com_modifiers : BoolProperty(
- name="Component Modifiers",
- default=False,
- description="Apply Modifiers and Shape Keys to the component object"
- )
- merge : BoolProperty(
- name="Merge",
- default=False,
- description="Merge vertices in adjacent duplicates"
- )
- merge_thres : FloatProperty(
- name="Distance",
- default=0.001,
- soft_min=0,
- soft_max=10,
- description="Limit below which to merge vertices"
- )
- bool_random : BoolProperty(
- name="Randomize",
- default=False,
- description="Randomize component rotation"
- )
- random_seed : IntProperty(
- name="Seed",
- default=0,
- soft_min=0,
- soft_max=10,
- description="Random seed"
- )
- bool_vertex_group : BoolProperty(
- name="Map Vertex Groups",
- default=False,
- description="Transfer all Vertex Groups from Base object"
- )
- bool_selection : BoolProperty(
- name="On selected Faces",
- default=False,
- description="Create Tessellation only on selected faces"
- )
- bool_shapekeys : BoolProperty(
- name="Use Shape Keys",
- default=False,
- description="Transfer Component's Shape Keys. If the name of Vertex "
- "Groups and Shape Keys are the same, they will be "
- "automatically combined"
- )
- bool_smooth : BoolProperty(
- name="Smooth Shading",
- default=False,
- description="Output faces with smooth shading rather than flat shaded"
- )
- bool_materials : BoolProperty(
- name="Transfer Materials",
- default=True,
- description="Preserve component's materials"
- )
- generator : StringProperty(
- name="",
- description="Base object for the tessellation",
- default = ""
- )
- component : StringProperty(
- name="",
- description="Component object for the tessellation",
- default = ""
- )
- bool_material_id : BoolProperty(
- name="Tessellation on Material ID",
- default=False,
- description="Apply the component only on the selected Material"
- )
- bool_dissolve_seams : BoolProperty(
- name="Dissolve Seams",
- default=False,
- description="Dissolve all seam edges"
- )
- material_id : IntProperty(
- name="Material ID",
- default=0,
- min=0,
- description="Material ID"
- )
- iterations : IntProperty(
- name="Iterations",
- default=1,
- min=1,
- soft_max=5,
- description="Automatically repeat the Tessellation using the "
- + "generated geometry as new base object.\nUsefull for "
- + "for branching systems. Dangerous!"
- )
- bool_combine : BoolProperty(
- name="Combine unused",
- default=False,
- description="Combine the generated geometry with unused faces"
- )
- bool_advanced : BoolProperty(
- name="Advanced Settings",
- default=False,
- description="Show more settings"
- )
- normals_mode : EnumProperty(
- items=(
- ('VERTS', 'Normals', 'Consistent direction based on vertices normal'),
- ('FACES', 'Individual Faces', 'Based on individual faces normal'),
- ('CUSTOM', 'Custom', "According to Base object's shape keys")),
- default='VERTS',
- name="Direction"
- )
- bool_multi_components : BoolProperty(
- name="Multi Components",
- default=False,
- description="Combine different components according to materials name"
- )
- bounds_x : EnumProperty(
- items=(
- ('EXTEND', 'Extend', 'Default X coordinates'),
- ('CLIP', 'Clip', 'Trim out of bounds in X direction'),
- ('CYCLIC', 'Cyclic', 'Cyclic components in X direction')),
- default='EXTEND',
- name="Bounds X",
- )
- bounds_y : EnumProperty(
- items=(
- ('EXTEND', 'Extend', 'Default Y coordinates'),
- ('CLIP', 'Clip', 'Trim out of bounds in Y direction'),
- ('CYCLIC', 'Cyclic', 'Cyclic components in Y direction')),
- default='EXTEND',
- name="Bounds Y",
- )
- close_mesh : EnumProperty(
- items=(
- ('NONE', 'None', 'Keep the mesh open'),
- ('CAP', 'Cap Holes', 'Automatically cap open loops'),
- ('BRIDGE', 'Bridge Loops', 'Automatically bridge loop pairs')),
- default='NONE',
- name="Close Mesh"
- )
- cap_faces : BoolProperty(
- name="Cap Holes",
- default=False,
- description="Cap open edges loops"
- )
- frame_boundary : BoolProperty(
- name="Frame Boundary",
- default=False,
- description="Support face boundaries"
- )
- fill_frame : BoolProperty(
- name="Fill Frame",
- default=False,
- description="Fill inner faces with Fan tessellation"
- )
- frame_boundary_mat : IntProperty(
- name="Material Offset",
- default=0,
- description="Material Offset for boundaries"
- )
- fill_frame_mat : IntProperty(
- name="Material Offset",
- default=0,
- description="Material Offset for inner faces"
- )
- open_edges_crease : FloatProperty(
- name="Open Edges Crease",
- default=0,
- min=0,
- max=1,
- description="Automatically set crease for open edges"
- )
- bridge_smoothness : FloatProperty(
- name="Smoothness",
- default=1,
- min=0,
- max=1,
- description="Bridge Smoothness"
- )
- frame_thickness : FloatProperty(
- name="Frame Thickness",
- default=0.2,
- min=0,
- soft_max=2,
- description="Frame Thickness"
- )
- frame_mode : EnumProperty(
- items=(
- ('CONSTANT', 'Constant', 'Even thickness'),
- ('RELATIVE', 'Relative', 'Frame offset depends on face areas')),
- default='CONSTANT',
- name="Offset"
- )
- bridge_cuts : IntProperty(
- name="Cuts",
- default=0,
- min=0,
- max=20,
- description="Bridge Cuts"
- )
- cap_material_index : IntProperty(
- name="Material",
- default=0,
- min=0,
- description="Material index for the cap/bridge faces"
- )
- patch_subs : IntProperty(
- name="Patch Subdivisions",
- default=1,
- min=0,
- description="Subdivisions levels for Patch tessellation after the first iteration"
- )
- working_on = ""
-
- def draw(self, context):
- allowed_obj = ('MESH', 'CURVE', 'SURFACE', 'FONT', 'META')
- '''
- try:
- bool_working = self.working_on == self.object_name and \
- self.working_on != ""
- except:
- bool_working = False
- '''
-
- bool_working = False
- bool_allowed = False
- ob0 = None
- ob1 = None
-
- sel = bpy.context.selected_objects
- if len(sel) == 1:
- try:
- ob0 = sel[0].tissue_tessellate.generator
- ob1 = sel[0].tissue_tessellate.component
- self.generator = ob0.name
- self.component = ob1.name
- if self.working_on == '':
- load_parameters(self,sel[0])
- self.working_on = sel[0].name
- bool_working = True
- bool_allowed = True
- except:
- pass
-
- if len(sel) == 2:
- bool_allowed = True
- for o in sel:
- if o.type not in allowed_obj:
- bool_allowed = False
-
- if len(sel) != 2 and not bool_working:
- layout = self.layout
- layout.label(icon='INFO')
- layout.label(text="Please, select two different objects")
- layout.label(text="Select first the Component object, then select")
- layout.label(text="the Base object.")
- elif not bool_allowed and not bool_working:
- layout = self.layout
- layout.label(icon='INFO')
- layout.label(text="Only Mesh, Curve, Surface or Text objects are allowed")
- else:
- if ob0 == ob1 == None:
- ob0 = bpy.context.active_object
- self.generator = ob0.name
- for o in sel:
- if o != ob0:
- ob1 = o
- self.component = o.name
- self.no_component = False
- break
-
- # new object name
- if self.object_name == "":
- if self.generator == "":
- self.object_name = "Tessellation"
- else:
- #self.object_name = self.generator + "_Tessellation"
- self.object_name = "Tessellation"
-
- layout = self.layout
- # Base and Component
- col = layout.column(align=True)
- row = col.row(align=True)
- row.label(text="BASE : " + self.generator)
- row.label(text="COMPONENT : " + self.component)
-
- # Base Modifiers
- row = col.row(align=True)
- col2 = row.column(align=True)
- col2.prop(self, "gen_modifiers", text="Use Modifiers", icon='MODIFIER')
- base = bpy.data.objects[self.generator]
- try:
- if not (base.modifiers or base.data.shape_keys):
- col2.enabled = False
- self.gen_modifiers = False
- except:
- col2.enabled = False
- self.gen_modifiers = False
-
- # Component Modifiers
- row.separator()
- col3 = row.column(align=True)
- col3.prop(self, "com_modifiers", text="Use Modifiers", icon='MODIFIER')
- component = bpy.data.objects[self.component]
- try:
- if not (component.modifiers or component.data.shape_keys):
- col3.enabled = False
- self.com_modifiers = False
- except:
- col3.enabled = False
- self.com_modifiers = False
- col.separator()
- # Fill and Rotation
- row = col.row(align=True)
- row.label(text="Fill Mode:")
- row = col.row(align=True)
- row.prop(
- self, "fill_mode", icon='NONE', expand=True,
- slider=True, toggle=False, icon_only=False, event=False,
- full_event=False, emboss=True, index=-1)
- row = col.row(align=True)
- row.prop(self, "bool_smooth")
-
- # frame settings
- if self.fill_mode == 'FRAME':
- col.separator()
- col.label(text="Frame Settings:")
- row = col.row(align=True)
- row.prop(self, "frame_mode", expand=True)
- col.prop(self, "frame_thickness", text='Thickness', icon='NONE')
- col.separator()
- row = col.row(align=True)
- row.prop(self, "fill_frame", icon='NONE')
- show_frame_mat = self.bool_multi_components or self.bool_material_id
- if self.fill_frame and show_frame_mat:
- row.prop(self, "fill_frame_mat", icon='NONE')
- row = col.row(align=True)
- row.prop(self, "frame_boundary", text='Boundary', icon='NONE')
- if self.frame_boundary and show_frame_mat:
- row.prop(self, "frame_boundary_mat", icon='NONE')
-
- if self.rotation_mode == 'UV':
- uv_error = False
-
- if ob0.type != 'MESH':
- row = col.row(align=True)
- row.label(
- text="UV rotation supported only for Mesh objects",
- icon='ERROR')
- uv_error = True
- else:
- if len(ob0.data.uv_layers) == 0:
- row = col.row(align=True)
- check_name = self.generator
- row.label(text="'" + check_name +
- "' doesn't have UV Maps", icon='ERROR')
- uv_error = True
- if uv_error:
- row = col.row(align=True)
- row.label(text="Default rotation will be used instead",
- icon='INFO')
-
- # Component Z
- col.separator()
- col.label(text="Thickness:")
- row = col.row(align=True)
- row.prop(
- self, "scale_mode", text="Scale Mode", icon='NONE', expand=True,
- slider=False, toggle=False, icon_only=False, event=False,
- full_event=False, emboss=True, index=-1)
- col.prop(
- self, "zscale", text="Scale", icon='NONE', expand=False,
- slider=True, toggle=False, icon_only=False, event=False,
- full_event=False, emboss=True, index=-1)
- if self.mode == 'BOUNDS':
- col.prop(
- self, "offset", text="Offset", icon='NONE', expand=False,
- slider=True, toggle=False, icon_only=False, event=False,
- full_event=False, emboss=True, index=-1)
-
- # Component XY
- col.separator()
- row = col.row(align=True)
- row.label(text="Component Coordinates:")
- row = col.row(align=True)
- row.prop(
- self, "mode", text="Component XY", icon='NONE', expand=True,
- slider=False, toggle=False, icon_only=False, event=False,
- full_event=False, emboss=True, index=-1)
-
- if self.mode != 'BOUNDS':
- col.separator()
- row = col.row(align=True)
- row.label(text="X:")
- row.prop(
- self, "bounds_x", text="Bounds X", icon='NONE', expand=True,
- slider=False, toggle=False, icon_only=False, event=False,
- full_event=False, emboss=True, index=-1)
-
- row = col.row(align=True)
- row.label(text="Y:")
- row.prop(
- self, "bounds_y", text="Bounds X", icon='NONE', expand=True,
- slider=False, toggle=False, icon_only=False, event=False,
- full_event=False, emboss=True, index=-1)
-
-
- # merge settings
- col = layout.column(align=True)
- row = col.row(align=True)
- row.prop(self, "merge")
- if self.merge:
- row.prop(self, "merge_thres")
- col.separator()
- row = col.row(align=True)
- col2 = row.column(align=True)
- col2.label(text='Close Mesh:')
- col2 = row.column(align=True)
- col2.prop(self, "close_mesh",text='')
- if self.close_mesh != 'NONE':
- row = col.row(align=True)
- row.prop(self, "open_edges_crease", text="Crease")
- row.prop(self, "cap_material_index")
- if self.close_mesh == 'BRIDGE':
- row = col.row(align=True)
- row.prop(self, "bridge_cuts")
- row.prop(self, "bridge_smoothness")
- row = col.row(align=True)
- row.prop(self, "bool_dissolve_seams")
-
- # Advanced Settings
- col = layout.column(align=True)
- col.separator()
- col.separator()
- row = col.row(align=True)
- row.prop(self, "bool_advanced", icon='SETTINGS')
- if self.bool_advanced:
- # rotation
- layout.use_property_split = True
- layout.use_property_decorate = False # No animation.
- col = layout.column(align=True)
- col.prop(self, "rotation_mode", text='Rotation', icon='NONE', expand=False,
- slider=True, toggle=False, icon_only=False, event=False,
- full_event=False, emboss=True, index=-1)
- if self.rotation_mode == 'WEIGHT':
- col.prop(self, "rotation_direction", expand=False,
- slider=True, toggle=False, icon_only=False, event=False,
- full_event=False, emboss=True, index=-1)
- if self.rotation_mode == 'RANDOM':
- col.prop(self, "random_seed")
- else:
- col.prop(self, "rotation_shift")
-
- if self.rotation_mode == 'UV':
- uv_error = False
- if self.generator.type != 'MESH':
- row = col.row(align=True)
- row.label(
- text="UV rotation supported only for Mesh objects",
- icon='ERROR')
- uv_error = True
- else:
- if len(self.generator.data.uv_layers) == 0:
- row = col.row(align=True)
- row.label(text="'" + props.generator.name +
- " doesn't have UV Maps", icon='ERROR')
- uv_error = True
- if uv_error:
- row = col.row(align=True)
- row.label(text="Default rotation will be used instead",
- icon='INFO')
- layout.use_property_split = False
-
- # Direction
- col = layout.column(align=True)
- row = col.row(align=True)
- row.label(text="Direction:")
- row = col.row(align=True)
- row.prop(
- self, "normals_mode", text="Direction", icon='NONE', expand=True,
- slider=False, toggle=False, icon_only=False, event=False,
- full_event=False, emboss=True, index=-1)
- #row.enabled = self.fill_mode != 'PATCH'
-
- allow_multi = False
- allow_shapekeys = not self.com_modifiers
- if self.com_modifiers: self.bool_shapekeys = False
- for m in ob0.data.materials:
- try:
- o = bpy.data.objects[m.name]
- allow_multi = True
- try:
- if o.data.shape_keys is None: continue
- elif len(o.data.shape_keys.key_blocks) < 2: continue
- else: allow_shapekeys = not self.com_modifiers
- except: pass
- except: pass
- # DATA #
- col = layout.column(align=True)
- col.label(text="Weight and Morphing:")
- # vertex group + shape keys
- row = col.row(align=True)
- col2 = row.column(align=True)
- col2.prop(self, "bool_vertex_group", icon='GROUP_VERTEX')
- try:
- if len(ob0.vertex_groups) == 0:
- col2.enabled = False
- except:
- col2.enabled = False
- row.separator()
- col2 = row.column(align=True)
- row2 = col2.row(align=True)
- row2.prop(self, "bool_shapekeys", text="Use Shape Keys", icon='SHAPEKEY_DATA')
- row2.enabled = allow_shapekeys
-
- # LIMITED TESSELLATION
- col = layout.column(align=True)
- col.label(text="Limited Tessellation:")
- row = col.row(align=True)
- col2 = row.column(align=True)
- col2.prop(self, "bool_multi_components", icon='MOD_TINT')
- if not allow_multi:
- col2.enabled = False
- self.bool_multi_components = False
- col.separator()
- row = col.row(align=True)
- col2 = row.column(align=True)
- col2.prop(self, "bool_selection", text="On selected Faces", icon='RESTRICT_SELECT_OFF')
- row.separator()
- if ob0.type != 'MESH':
- col2.enabled = False
- col2 = row.column(align=True)
- col2.prop(self, "bool_material_id", icon='MATERIAL_DATA', text="Material ID")
- if self.bool_material_id and not self.bool_multi_components:
- #col2 = row.column(align=True)
- col2.prop(self, "material_id")
- col2.enabled = not self.bool_multi_components
-
- col.separator()
- row = col.row(align=True)
- row.label(text='Reiterate Tessellation:', icon='FILE_REFRESH')
- row.prop(self, 'iterations', text='Repeat', icon='SETTINGS')
- if self.iterations > 1 and self.fill_mode == 'PATCH':
- col.separator()
- row = col.row(align=True)
- row.prop(self, 'patch_subs')
- col.separator()
- row = col.row(align=True)
- row.label(text='Combine Iterations:')
- row = col.row(align=True)
- row.prop(
- self, "combine_mode", icon='NONE', expand=True,
- slider=False, toggle=False, icon_only=False, event=False,
- full_event=False, emboss=True, index=-1)
-
- def execute(self, context):
- allowed_obj = ('MESH', 'CURVE', 'META', 'SURFACE', 'FONT')
- try:
- ob0 = bpy.data.objects[self.generator]
- ob1 = bpy.data.objects[self.component]
- except:
- return {'CANCELLED'}
-
- self.object_name = "Tessellation"
- # Check if existing object with same name
- names = [o.name for o in bpy.data.objects]
- if self.object_name in names:
- count_name = 1
- while True:
- test_name = self.object_name + '.{:03d}'.format(count_name)
- if not (test_name in names):
- self.object_name = test_name
- break
- count_name += 1
-
- if ob1.type not in allowed_obj:
- message = "Component must be Mesh, Curve, Surface, Text or Meta object!"
- self.report({'ERROR'}, message)
- self.component = None
-
- if ob0.type not in allowed_obj:
- message = "Generator must be Mesh, Curve, Surface, Text or Meta object!"
- self.report({'ERROR'}, message)
- self.generator = ""
-
- if True:#self.component not in ("",None) and self.generator not in ("",None):
- if bpy.ops.object.select_all.poll():
- bpy.ops.object.select_all(action='TOGGLE')
- bpy.ops.object.mode_set(mode='OBJECT')
-
- #data0 = ob0.to_mesh(False)
- #data0 = ob0.data.copy()
- bool_update = False
- if bpy.context.object == ob0:
- auto_layer_collection()
- #new_ob = bpy.data.objects.new(self.object_name, data0)
- new_ob = convert_object_to_mesh(ob0,False,False)
- new_ob.data.name = self.object_name
- #bpy.context.collection.objects.link(new_ob)
- #bpy.context.view_layer.objects.active = new_ob
- new_ob.name = self.object_name
- #new_ob.select_set(True)
- else:
- new_ob = bpy.context.object
- bool_update = True
- new_ob = store_parameters(self, new_ob)
- try: bpy.ops.object.tissue_update_tessellate()
- except RuntimeError as e:
- bpy.data.objects.remove(new_ob)
- self.report({'ERROR'}, str(e))
- return {'CANCELLED'}
- if not bool_update:
- self.object_name = new_ob.name
- #self.working_on = self.object_name
- new_ob.location = ob0.location
- new_ob.matrix_world = ob0.matrix_world
-
- return {'FINISHED'}
-
- def invoke(self, context, event):
- return context.window_manager.invoke_props_dialog(self)
-
-
- def update_dependencies(ob, objects):
- ob0 = ob.tissue_tessellate.generator
- ob1 = ob.tissue_tessellate.component
- deps = [ob0, ob1]
- for o in deps:
- if o.tissue_tessellate.bool_lock: continue
- o0 = o.tissue_tessellate.generator
- o1 = o.tissue_tessellate.component
- deps_deps = [o0, o1]
- try:
- o0.name
- o1.name
- if o0 not in objects and o1 not in objects:
- objects.append(o)
- objects = update_dependencies(o, objects)
- except:
- continue
- return objects
-
-
- class tissue_refresh_tessellate(Operator):
- bl_idname = "object.tissue_refresh_tessellate"
- bl_label = "Refresh"
- bl_description = ("Fast update the tessellated mesh according to base and "
- "component changes")
- bl_options = {'REGISTER', 'UNDO'}
-
- go = False
-
- @classmethod
- def poll(cls, context):
- try:
- return context.object.tissue_tessellate.generator != None and \
- context.object.tissue_tessellate.component != None
- except:
- return False
-
- @staticmethod
- def check_gen_comp(checking):
- # note pass the stored name key in here to check it out
- return checking in bpy.data.objects.keys()
-
- def execute(self, context):
- ob = bpy.context.object
- ob0 = ob.tissue_tessellate.generator
- ob1 = ob.tissue_tessellate.component
- try:
- ob0.name
- ob1.name
- except:
- self.report({'ERROR'},
- "Active object must be Tessellate before Update")
- return {'CANCELLED'}
-
- if ob.tissue_tessellate.bool_dependencies:
- update_objects = list(reversed(update_dependencies(ob, [ob])))
- else:
- update_objects = [ob]
- for o in update_objects:
- override = {'object': o}
- bpy.ops.object.tissue_update_tessellate(override)
-
- return {'FINISHED'}
-
-
- class tissue_update_tessellate(Operator):
- bl_idname = "object.tissue_update_tessellate"
- bl_label = "Refresh"
- bl_description = ("Fast update the tessellated mesh according to base and "
- "component changes")
- bl_options = {'REGISTER', 'UNDO'}
-
- go = False
-
- @classmethod
- def poll(cls, context):
- #try:
- try: #context.object == None: return False
- return context.object.tissue_tessellate.generator != None and \
- context.object.tissue_tessellate.component != None
- except:
- return False
-
- @staticmethod
- def check_gen_comp(checking):
- # note pass the stored name key in here to check it out
- return checking in bpy.data.objects.keys()
-
- def execute(self, context):
- start_time = time.time()
-
- ob = context.object
- if not self.go:
- generator = ob.tissue_tessellate.generator
- component = ob.tissue_tessellate.component
- zscale = ob.tissue_tessellate.zscale
- scale_mode = ob.tissue_tessellate.scale_mode
- rotation_mode = ob.tissue_tessellate.rotation_mode
- rotation_shift = ob.tissue_tessellate.rotation_shift
- rotation_direction = ob.tissue_tessellate.rotation_direction
- offset = ob.tissue_tessellate.offset
- merge = ob.tissue_tessellate.merge
- merge_thres = ob.tissue_tessellate.merge_thres
- gen_modifiers = ob.tissue_tessellate.gen_modifiers
- com_modifiers = ob.tissue_tessellate.com_modifiers
- bool_random = ob.tissue_tessellate.bool_random
- random_seed = ob.tissue_tessellate.random_seed
- fill_mode = ob.tissue_tessellate.fill_mode
- bool_vertex_group = ob.tissue_tessellate.bool_vertex_group
- bool_selection = ob.tissue_tessellate.bool_selection
- bool_shapekeys = ob.tissue_tessellate.bool_shapekeys
- mode = ob.tissue_tessellate.mode
- bool_smooth = ob.tissue_tessellate.bool_smooth
- bool_materials = ob.tissue_tessellate.bool_materials
- bool_dissolve_seams = ob.tissue_tessellate.bool_dissolve_seams
- bool_material_id = ob.tissue_tessellate.bool_material_id
- material_id = ob.tissue_tessellate.material_id
- iterations = ob.tissue_tessellate.iterations
- bool_combine = ob.tissue_tessellate.bool_combine
- normals_mode = ob.tissue_tessellate.normals_mode
- bool_advanced = ob.tissue_tessellate.bool_advanced
- bool_multi_components = ob.tissue_tessellate.bool_multi_components
- combine_mode = ob.tissue_tessellate.combine_mode
- bounds_x = ob.tissue_tessellate.bounds_x
- bounds_y = ob.tissue_tessellate.bounds_y
- cap_faces = ob.tissue_tessellate.cap_faces
- close_mesh = ob.tissue_tessellate.close_mesh
- open_edges_crease = ob.tissue_tessellate.open_edges_crease
- bridge_smoothness = ob.tissue_tessellate.bridge_smoothness
- frame_thickness = ob.tissue_tessellate.frame_thickness
- frame_mode = ob.tissue_tessellate.frame_mode
- frame_boundary = ob.tissue_tessellate.frame_boundary
- fill_frame = ob.tissue_tessellate.fill_frame
- frame_boundary_mat = ob.tissue_tessellate.frame_boundary_mat
- fill_frame_mat = ob.tissue_tessellate.fill_frame_mat
- bridge_cuts = ob.tissue_tessellate.bridge_cuts
- cap_material_index = ob.tissue_tessellate.cap_material_index
- patch_subs = ob.tissue_tessellate.patch_subs
- try:
- generator.name
- component.name
- except:
- self.report({'ERROR'},
- "Active object must be Tessellate before Update")
- return {'CANCELLED'}
-
- # Solve Local View issues
- local_spaces = []
- local_ob0 = []
- local_ob1 = []
- for area in context.screen.areas:
- for space in area.spaces:
- try:
- if ob.local_view_get(space):
- local_spaces.append(space)
- local_ob0 = ob0.local_view_get(space)
- ob0.local_view_set(space, True)
- local_ob1 = ob1.local_view_get(space)
- ob1.local_view_set(space, True)
- except:
- pass
-
- starting_mode = context.object.mode
-
- #if starting_mode == 'PAINT_WEIGHT': starting_mode = 'WEIGHT_PAINT'
- bpy.ops.object.mode_set(mode='OBJECT')
-
- ob0 = generator
- ob1 = component
- ##### auto_layer_collection()
-
- ob0_hide = ob0.hide_get()
- ob0_hidev = ob0.hide_viewport
- ob0_hider = ob0.hide_render
- ob1_hide = ob1.hide_get()
- ob1_hidev = ob1.hide_viewport
- ob1_hider = ob1.hide_render
- ob0.hide_set(False)
- ob0.hide_viewport = False
- ob0.hide_render = False
- ob1.hide_set(False)
- ob1.hide_viewport = False
- ob1.hide_render = False
-
- if ob0.type == 'META':
- base_ob = convert_object_to_mesh(ob0, False, True)
- else:
- base_ob = ob0.copy()
- base_ob.data = ob0.data#
- context.collection.objects.link(base_ob)
- base_ob.name = '_tissue_tmp_base'
-
- # In Blender 2.80 cache of copied objects is lost, must be re-baked
- bool_update_cloth = False
- for m in base_ob.modifiers:
- if m.type == 'CLOTH':
- m.point_cache.frame_end = context.scene.frame_current
- bool_update_cloth = True
- if bool_update_cloth:
- bpy.ops.ptcache.free_bake_all()
- bpy.ops.ptcache.bake_all()
- base_ob.modifiers.update()
-
-
- #new_ob.location = ob.location
- #new_ob.matrix_world = ob.matrix_world
- #bpy.ops.object.select_all(action='DESELECT')
- if bool_selection:
- faces = base_ob.data.polygons
- selections = [False]*len(faces)
- faces.foreach_get('select',selections)
- selections = np.array(selections)
- if not selections.any():
- message = "There are no faces selected."
- context.view_layer.objects.active = ob
- ob.select_set(True)
- bpy.ops.object.mode_set(mode=starting_mode)
- bpy.data.objects.remove(base_ob)
- self.report({'ERROR'}, message)
- return {'CANCELLED'}
-
-
-
- iter_objects = [base_ob]
- ob_location = ob.location
- ob_matrix_world = ob.matrix_world
- #base_ob = new_ob#.copy()
-
- for iter in range(iterations):
-
- if iter > 0 and len(iter_objects) == 0: break
- if iter > 0 and normals_mode == 'CUSTOM': normals_mode = 'VERTS'
- same_iteration = []
- matched_materials = []
- # iterate base object materials (needed for multi-components)
- if bool_multi_components: mat_iter = len(base_ob.material_slots)
- else: mat_iter = 1
- for m_id in range(mat_iter):
- if bool_multi_components:
- # check if material and components match
- try:
- mat = base_ob.material_slots[m_id].material
- ob1 = bpy.data.objects[mat.name]
- if ob1.type not in ('MESH', 'CURVE','SURFACE','FONT', 'META'):
- continue
- material_id = m_id
- matched_materials.append(m_id)
- bool_material_id = True
- except:
- continue
- if com_modifiers or ob1.type != 'MESH':
- data1 = simple_to_mesh(ob1)
- else:
- data1 = ob1.data.copy()
- n_edges1 = len(data1.edges)
- bpy.data.meshes.remove(data1)
-
- if iter != 0: gen_modifiers = True
-
- if fill_mode == 'PATCH':
- # patch subdivisions for additional iterations
- if iter > 0:
- base_ob.modifiers.new('Tissue_Subsurf', type='SUBSURF')
- base_ob.modifiers['Tissue_Subsurf'].levels = patch_subs
- temp_mod = base_ob.modifiers['Tissue_Subsurf']
- # patch tessellation
- new_ob = tessellate_patch(
- base_ob, ob1, offset, zscale, com_modifiers, mode, scale_mode,
- rotation_mode, rotation_shift, random_seed, bool_vertex_group,
- bool_selection, bool_shapekeys, bool_material_id, material_id,
- normals_mode, bounds_x, bounds_y
- )
- if iter > 0:
- base_ob.modifiers.remove(temp_mod)
- else:
- ### FRAME and FAN ###
- if fill_mode in ('FRAME','FAN'):
-
- if fill_mode == 'FRAME': convert_function = convert_to_frame
- else: convert_function = convert_to_fan
-
- if normals_mode == 'CUSTOM' and base_ob.data.shape_keys != None:
- ## base key
- sk_values = [sk.value for sk in base_ob.data.shape_keys.key_blocks]
- for sk in ob0.data.shape_keys.key_blocks: sk.value = 0
- _base_ob = convert_function(base_ob, ob.tissue_tessellate, gen_modifiers)
- for i, sk in enumerate(ob0.data.shape_keys.key_blocks):
- sk.value = sk_values[i]
- ## key 1
- # hide modifiers
- if not gen_modifiers and len(base_ob.modifiers) > 0:
- mod_visibility = [m.show_viewport for m in base_ob.modifiers]
- for m in base_ob.modifiers: m.show_viewport = False
- base_ob.modifiers.update()
- base_ob_sk = convert_function(ob0, ob.tissue_tessellate, True)
- ## combine shapekeys
- _base_ob.shape_key_add(name='Basis', from_mix=False)
- _base_ob.shape_key_add(name='Key1', from_mix=False)
- sk_block = _base_ob.data.shape_keys.key_blocks[1]
- sk_block.value = 1
- for vert, sk in zip(base_ob_sk.data.vertices, sk_block.data):
- sk.co = vert.co
- bpy.data.objects.remove(base_ob_sk)
- # set original modifiers
- if not gen_modifiers and len(base_ob.modifiers) > 0:
- for i,m in enumerate(base_ob.modifiers):
- m.show_viewport = mod_visibility[i]
- base_ob.modifiers.update()
- else:
- _base_ob = convert_function(base_ob, ob.tissue_tessellate, gen_modifiers)
- bpy.data.objects.remove(base_ob)
- base_ob = _base_ob
- # quad tessellation
- new_ob = tessellate_original(
- base_ob, ob1, offset, zscale, gen_modifiers,
- com_modifiers, mode, scale_mode, rotation_mode,
- rotation_shift, rotation_direction,
- random_seed, fill_mode, bool_vertex_group,
- bool_selection, bool_shapekeys, bool_material_id,
- material_id, normals_mode, bounds_x, bounds_y
- )
-
- # if empty or error, continue
- if type(new_ob) is not bpy.types.Object:
- continue
-
- # prepare base object
- if iter == 0 and gen_modifiers:
- temp_base_ob = convert_object_to_mesh(base_ob, True, True)
- bpy.data.objects.remove(base_ob)
- base_ob = temp_base_ob
- iter_objects = [base_ob]
-
- # rename, make active and change transformations
- new_ob.name = '_tissue_tmp_{}_{}'.format(iter,m_id)
- new_ob.select_set(True)
- context.view_layer.objects.active = new_ob
- new_ob.location = ob_location
- new_ob.matrix_world = ob_matrix_world
-
- n_components = int(len(new_ob.data.edges) / n_edges1)
- # SELECTION
- if bool_selection:
- try:
- # create selection list
- polygon_selection = [p.select for p in ob1.data.polygons] * int(
- len(new_ob.data.polygons) / len(ob1.data.polygons))
- new_ob.data.polygons.foreach_set("select", polygon_selection)
- except:
- pass
- if bool_multi_components: same_iteration.append(new_ob)
-
- base_ob.location = ob_location
- base_ob.matrix_world = ob_matrix_world
-
- # join together multiple components iterations
- if bool_multi_components:
- if len(same_iteration) > 0:
- context.view_layer.update()
- for o in context.view_layer.objects:
- o.select_set(o in same_iteration)
- bpy.ops.object.join()
- new_ob = context.view_layer.objects.active
- new_ob.select_set(True)
- #new_ob.data.update()
-
- if type(new_ob) in (int,str):
- if iter == 0:
- try:
- bpy.data.objects.remove(iter_objects[0])
- iter_objects = []
- except: continue
- continue
-
- # Clean last iteration, needed for combine object
- if (bool_selection or bool_material_id) and combine_mode == 'UNUSED':
- # remove faces from last mesh
- bm = bmesh.new()
- last_mesh = iter_objects[-1].data.copy()
- bm.from_mesh(last_mesh)
- bm.faces.ensure_lookup_table()
- if bool_multi_components:
- remove_materials = matched_materials
- elif bool_material_id:
- remove_materials = [material_id]
- else: remove_materials = []
- if bool_selection:
- if bool_multi_components or bool_material_id:
- remove_faces = [f for f in bm.faces if f.material_index in remove_materials and f.select]
- else:
- remove_faces = [f for f in bm.faces if f.select]
- else:
- remove_faces = [f for f in bm.faces if f.material_index in remove_materials]
- bmesh.ops.delete(bm, geom=remove_faces, context='FACES')
- bm.to_mesh(last_mesh)
- last_mesh.update()
- last_mesh.name = '_tissue_tmp_previous_unused'
-
- # delete previous iteration if empty or update it
- if len(last_mesh.vertices) > 0:
- iter_objects[-1].data = last_mesh.copy()
- iter_objects[-1].data.update()
- else:
- bpy.data.objects.remove(iter_objects[-1])
- iter_objects = iter_objects[:-1]
- # set new base object for next iteration
- base_ob = convert_object_to_mesh(new_ob,True,True)
- if iter < iterations-1: new_ob.data = base_ob.data
- # store new iteration and set transformations
- iter_objects.append(new_ob)
- #try:
- # bpy.data.objects.remove(bpy.data.objects['_tissue_tmp_base'])
- #except:
- # pass
- base_ob.name = '_tissue_tmp_base'
- elif combine_mode == 'ALL':
- base_ob = new_ob.copy()
- iter_objects.append(new_ob)
- else:
- if base_ob != new_ob:
- bpy.data.objects.remove(base_ob)
- base_ob = new_ob
- iter_objects = [new_ob]
-
- # Combine
- if combine_mode != 'LAST' and len(iter_objects)>0:
- if base_ob not in iter_objects and type(base_ob) == bpy.types.Object:
- bpy.data.objects.remove(base_ob)
- for o in context.view_layer.objects:
- o.select_set(o in iter_objects)
- bpy.ops.object.join()
- new_ob.data.update()
- iter_objects = [new_ob]
-
- if merge:
- bpy.ops.object.mode_set(mode='EDIT')
- bpy.ops.mesh.select_mode(
- use_extend=False, use_expand=False, type='VERT')
- bpy.ops.mesh.select_non_manifold(
- extend=False, use_wire=True, use_boundary=True,
- use_multi_face=False, use_non_contiguous=False, use_verts=False)
-
- bpy.ops.mesh.remove_doubles(
- threshold=merge_thres, use_unselected=False)
-
- if bool_dissolve_seams:
- bpy.ops.mesh.select_mode(type='EDGE')
- bpy.ops.mesh.select_all(action='DESELECT')
- bpy.ops.object.mode_set(mode='OBJECT')
- for e in new_ob.data.edges:
- e.select = e.use_seam
- bpy.ops.object.mode_set(mode='EDIT')
- bpy.ops.mesh.dissolve_edges()
- bpy.ops.object.mode_set(mode='OBJECT')
-
- if close_mesh != 'NONE':
- bpy.ops.object.mode_set(mode='EDIT')
- bpy.ops.mesh.select_mode(
- use_extend=False, use_expand=False, type='EDGE')
- bpy.ops.mesh.select_non_manifold(
- extend=False, use_wire=False, use_boundary=True,
- use_multi_face=False, use_non_contiguous=False, use_verts=False)
- if open_edges_crease != 0:
- bpy.ops.transform.edge_crease(value=open_edges_crease)
- if close_mesh == 'CAP':
- bpy.ops.mesh.edge_face_add()
- if close_mesh == 'BRIDGE':
- try:
- bpy.ops.mesh.bridge_edge_loops(
- type='PAIRS',
- number_cuts=bridge_cuts,
- interpolation='SURFACE',
- smoothness=bridge_smoothness)
- except: pass
- bpy.ops.object.mode_set(mode='OBJECT')
- for f in new_ob.data.polygons:
- if f.select: f.material_index = cap_material_index
- base_ob = context.view_layer.objects.active
-
- # Combine iterations
- if combine_mode != 'LAST' and len(iter_objects)>0:
- #if base_ob not in iter_objects and type(base_ob) == bpy.types.Object:
- # bpy.data.objects.remove(base_ob)
- for o in context.view_layer.objects:
- o.select_set(o in iter_objects)
- bpy.ops.object.join()
- new_ob = context.view_layer.objects.active
- elif combine_mode == 'LAST' and type(new_ob) != bpy.types.Object:
- # if last iteration gives error, then use the last correct iteration
- try:
- if type(iter_objects[-1]) == bpy.types.Object:
- new_ob = iter_objects[-1]
- except: pass
-
- if new_ob == 0:
- #bpy.data.objects.remove(base_ob.data)
- try: bpy.data.objects.remove(base_ob)
- except: pass
- message = "The generated object is an empty geometry!"
- context.view_layer.objects.active = ob
- ob.select_set(True)
- bpy.ops.object.mode_set(mode=starting_mode)
- self.report({'ERROR'}, message)
- return {'CANCELLED'}
- errors = {}
- errors["modifiers_error"] = "Modifiers that change the topology of the mesh \n" \
- "after the last Subsurf (or Multires) are not allowed."
- errors["topology_error"] = "Make sure that the topology of the mesh before \n" \
- "the last Subsurf (or Multires) is quads only."
- errors["wires_error"] = "Please remove all wire edges in the base object."
- errors["verts_error"] = "Please remove all floating vertices in the base object"
- if new_ob in errors:
- for o in iter_objects:
- try: bpy.data.objects.remove(o)
- except: pass
- try: bpy.data.meshes.remove(data1)
- except: pass
- context.view_layer.objects.active = ob
- ob.select_set(True)
- message = errors[new_ob]
- ob.tissue_tessellate.error_message = message
- bpy.ops.object.mode_set(mode=starting_mode)
- self.report({'ERROR'}, message)
- return {'CANCELLED'}
-
- #new_ob.location = ob_location
- #new_ob.matrix_world = ob_matrix_world
-
- # update data and preserve name
- if ob.type != 'MESH':
- loc, matr = ob.location, ob.matrix_world
- ob = convert_object_to_mesh(ob,False,True)
- ob.location, ob.matrix_world = loc, matr
- data_name = ob.data.name
- old_data = ob.data
- #ob.data = bpy.data.meshes.new_from_object(new_ob)#
- ob.data = new_ob.data.copy()
- ob.data.name = data_name
- bpy.data.meshes.remove(old_data)
-
- # copy vertex group
- if bool_vertex_group:
- for vg in new_ob.vertex_groups:
- if not vg.name in ob.vertex_groups.keys():
- ob.vertex_groups.new(name=vg.name)
- new_vg = ob.vertex_groups[vg.name]
- for i in range(len(ob.data.vertices)):
- try:
- weight = vg.weight(i)
- except:
- weight = 0
- new_vg.add([i], weight, 'REPLACE')
-
- selected_objects = [o for o in context.selected_objects]
- for o in selected_objects: o.select_set(False)
-
- ob.select_set(True)
- context.view_layer.objects.active = ob
-
- if merge:
- try:
- bpy.ops.object.mode_set(mode='EDIT')
- #bpy.ops.mesh.select_mode(
- # use_extend=False, use_expand=False, type='VERT')
- bpy.ops.mesh.select_mode(type='VERT')
- bpy.ops.mesh.select_non_manifold(
- extend=False, use_wire=True, use_boundary=True,
- use_multi_face=False, use_non_contiguous=False, use_verts=False)
-
- bpy.ops.mesh.remove_doubles(
- threshold=merge_thres, use_unselected=False)
-
- bpy.ops.object.mode_set(mode='OBJECT')
- if bool_dissolve_seams:
- bpy.ops.object.mode_set(mode='EDIT')
- bpy.ops.mesh.select_mode(type='EDGE')
- bpy.ops.mesh.select_all(action='DESELECT')
- bpy.ops.object.mode_set(mode='OBJECT')
- for e in ob.data.edges:
- e.select = e.use_seam
- bpy.ops.object.mode_set(mode='EDIT')
- bpy.ops.mesh.dissolve_edges()
- except: pass
- if close_mesh != 'NONE':
- bpy.ops.object.mode_set(mode='EDIT')
- bpy.ops.mesh.select_mode(
- use_extend=False, use_expand=False, type='EDGE')
- bpy.ops.mesh.select_non_manifold(
- extend=False, use_wire=False, use_boundary=True,
- use_multi_face=False, use_non_contiguous=False, use_verts=False)
- if open_edges_crease != 0:
- bpy.ops.transform.edge_crease(value=open_edges_crease)
- if close_mesh == 'CAP':
- bpy.ops.mesh.edge_face_add()
- if close_mesh == 'BRIDGE':
- try:
- bpy.ops.mesh.bridge_edge_loops(
- type='PAIRS',
- number_cuts=bridge_cuts,
- interpolation='SURFACE',
- smoothness=bridge_smoothness)
- except:
- pass
- bpy.ops.object.mode_set(mode='OBJECT')
- for f in ob.data.polygons:
- if f.select: f.material_index = cap_material_index
- #else:
-
- try:
- bpy.ops.object.mode_set(mode='EDIT')
- bpy.ops.object.mode_set(mode='OBJECT')
- except: pass
-
- if bool_smooth: bpy.ops.object.shade_smooth()
-
- for mesh in bpy.data.meshes:
- if not mesh.users: bpy.data.meshes.remove(mesh)
-
- for o in selected_objects:
- try: o.select_set(True)
- except: pass
-
- bpy.ops.object.mode_set(mode=starting_mode)
-
- ob.tissue_tessellate.error_message = ""
-
- # Restore Base visibility
- ob0.hide_set(ob0_hide)
- ob0.hide_viewport = ob0_hidev
- ob0.hide_render = ob0_hider
- # Restore Component visibility
- ob1.hide_set(ob1_hide)
- ob1.hide_viewport = ob1_hidev
- ob1.hide_render = ob1_hider
- # Restore Local visibility
- for space, local0, local1 in zip(local_spaces, local_ob0, local_ob1):
- ob0.local_view_set(space, local0)
- ob1.local_view_set(space, local1)
-
- bpy.data.objects.remove(new_ob)
-
- # clean objects
- for o in bpy.data.objects:
- #if o.name not in context.view_layer.objects and "_tissue_tmp" in o.name:
- if "_tissue_tmp" in o.name:
- bpy.data.objects.remove(o)
-
- end_time = time.time()
- print('Tissue: object "{}" tessellated in {:.4f} sec'.format(ob.name, end_time-start_time))
- return {'FINISHED'}
-
- def check(self, context):
- return True
-
- class TISSUE_PT_tessellate(Panel):
- bl_label = "Tissue Tools"
- bl_category = "Tissue"
- bl_space_type = "VIEW_3D"
- bl_region_type = "UI"
- #bl_options = {'DEFAULT_OPEN'}
-
- @classmethod
- def poll(cls, context):
- return context.mode in {'OBJECT', 'EDIT_MESH'}
-
- def draw(self, context):
- layout = self.layout
-
- col = layout.column(align=True)
- col.label(text="Tessellate:")
- col.operator("object.tissue_tessellate")
- col.operator("object.dual_mesh_tessellated")
- col.separator()
- col.operator("object.tissue_refresh_tessellate", icon='FILE_REFRESH')
-
- col.separator()
- col.label(text="Rotate Faces:")
- row = col.row(align=True)
- row.operator("mesh.tissue_rotate_face_left", text='Left', icon='LOOP_BACK')
- row.operator("mesh.tissue_rotate_face_right", text='Right', icon='LOOP_FORWARDS')
-
- col.separator()
- col.label(text="Other:")
- col.operator("object.dual_mesh")
- col.operator("object.lattice_along_surface", icon="OUTLINER_OB_LATTICE")
-
- act = context.object
- if act and act.type == 'MESH':
- col.operator("object.uv_to_mesh", icon="UV")
-
- if act.mode == 'EDIT':
- col.separator()
- col.label(text="Weight:")
- col.operator("object.tissue_weight_distance", icon="TRACKING")
-
- class TISSUE_PT_tessellate_object(Panel):
- bl_space_type = 'PROPERTIES'
- bl_region_type = 'WINDOW'
- bl_context = "data"
- bl_label = "Tessellate Settings"
- bl_options = {'DEFAULT_CLOSED'}
-
- @classmethod
- def poll(cls, context):
- try: return context.object.type == 'MESH'
- except: return False
-
- def draw(self, context):
- ob = context.object
- props = ob.tissue_tessellate
- allowed_obj = ('MESH','CURVE','SURFACE','FONT', 'META')
-
- try:
- bool_tessellated = props.generator or props.component != None
- ob0 = props.generator
- ob1 = props.component
- except: bool_tessellated = False
- layout = self.layout
- if not bool_tessellated:
- layout.label(text="The selected object is not a Tessellated object",
- icon='INFO')
- else:
- if props.error_message != "":
- layout.label(text=props.error_message,
- icon='ERROR')
- col = layout.column(align=True)
- row = col.row(align=True)
-
- set_tessellate_handler(self,context)
- set_animatable_fix_handler(self,context)
- row.operator("object.tissue_refresh_tessellate", icon='FILE_REFRESH')
- lock_icon = 'LOCKED' if props.bool_lock else 'UNLOCKED'
- #lock_icon = 'PINNED' if props.bool_lock else 'UNPINNED'
- deps_icon = 'LINKED' if props.bool_dependencies else 'UNLINKED'
- row.prop(props, "bool_dependencies", text="", icon=deps_icon)
- row.prop(props, "bool_lock", text="", icon=lock_icon)
- col2 = row.column(align=True)
- col2.prop(props, "bool_run", text="",icon='TIME')
- col2.enabled = not props.bool_lock
- '''
- col = layout.column(align=True)
- row = col.row(align=True)
- row.label(text="Base :")
- row.label(text="Component :")
- row = col.row(align=True)
-
- col2 = row.column(align=True)
- col2.prop_search(props, "generator", context.scene, "objects")
- row.separator()
- col2 = row.column(align=True)
- col2.prop_search(props, "component", context.scene, "objects")
- row = col.row(align=True)
- col2 = row.column(align=True)
- col2.prop(props, "gen_modifiers", text="Use Modifiers", icon='MODIFIER')
- row.separator()
- try:
- if not (ob0.modifiers or ob0.data.shape_keys) or props.fill_mode == 'PATCH':
- col2.enabled = False
- except:
- col2.enabled = False
- col2 = row.column(align=True)
- col2.prop(props, "com_modifiers", text="Use Modifiers", icon='MODIFIER')
- try:
- if not (props.component.modifiers or props.component.data.shape_keys):
- col2.enabled = False
- except:
- col2.enabled = False
- '''
- layout.use_property_split = True
- layout.use_property_decorate = False # No animation.
- col = layout.column(align=True)
- row = col.row(align=True)
- row.label(text='Base:')
- row.prop_search(props, "generator", context.scene, "objects")
- col2 = row.column(align=True)
- col2.prop(props, "gen_modifiers", text='',icon='MODIFIER')
- try:
- if not (props.generator.modifiers or props.generator.data.shape_keys):
- col2.enabled = False
- except:
- col2.enabled = False
- col.separator()
- row = col.row(align=True)
- row.label(text='Component:')
- row.prop_search(props, "component", context.scene, "objects")
- col2 = row.column(align=True)
- col2.prop(props, "com_modifiers", text='',icon='MODIFIER')
- try:
- if not (props.component.modifiers or props.component.data.shape_keys):
- col2.enabled = False
- except:
- col2.enabled = False
- layout.use_property_split = False
-
- # Fill
- col = layout.column(align=True)
- col.label(text="Fill Mode:")
-
- # fill
- row = col.row(align=True)
- row.prop(props, "fill_mode", icon='NONE', expand=True,
- slider=True, toggle=False, icon_only=False, event=False,
- full_event=False, emboss=True, index=-1)
-
- #layout.use_property_split = True
- col = layout.column(align=True)
- col.prop(props, "bool_smooth")
-
-
- class TISSUE_PT_tessellate_frame(Panel):
- bl_space_type = 'PROPERTIES'
- bl_region_type = 'WINDOW'
- bl_context = "data"
- bl_parent_id = "TISSUE_PT_tessellate_object"
- bl_label = "Frame Settings"
- #bl_options = {'DEFAULT_CLOSED'}
-
- @classmethod
- def poll(cls, context):
- try:
- bool_frame = context.object.tissue_tessellate.fill_mode == 'FRAME'
- bool_tessellated = context.object.tissue_tessellate.generator != None
- return context.object.type == 'MESH' and bool_frame and bool_tessellated
- except:
- return False
-
- def draw(self, context):
- ob = context.object
- props = ob.tissue_tessellate
- allowed_obj = ('MESH','CURVE','SURFACE','FONT', 'META')
-
- try:
- bool_tessellated = props.generator or props.component != None
- ob0 = props.generator
- ob1 = props.component
- except: bool_tessellated = False
- layout = self.layout
- if bool_tessellated:
- col = layout.column(align=True)
- row = col.row(align=True)
- row.prop(props, "frame_mode", expand=True)
- row = col.row(align=True)
- row.prop(props, "frame_thickness", icon='NONE', expand=True)
- row = col.row(align=True)
- row.prop(props, "fill_frame", icon='NONE')
- show_frame_mat = props.bool_multi_components or props.bool_material_id
- if props.fill_frame and show_frame_mat:
- row.prop(props, "fill_frame_mat", icon='NONE')
- row = col.row(align=True)
- row.prop(props, "frame_boundary", text='Boundary', icon='NONE')
- if props.frame_boundary and show_frame_mat:
- row.prop(props, "frame_boundary_mat", icon='NONE')
-
-
- class TISSUE_PT_tessellate_coordinates(Panel):
- bl_space_type = 'PROPERTIES'
- bl_region_type = 'WINDOW'
- bl_context = "data"
- bl_parent_id = "TISSUE_PT_tessellate_object"
- bl_label = "Component Coordinates"
- bl_options = {'DEFAULT_CLOSED'}
-
- @classmethod
- def poll(cls, context):
- try:
- bool_tessellated = context.object.tissue_tessellate.generator != None
- return context.object.type == 'MESH' and bool_tessellated
- except:
- return False
-
- def draw(self, context):
- ob = context.object
- props = ob.tissue_tessellate
- allowed_obj = ('MESH','CURVE','SURFACE','FONT', 'META')
-
- try:
- bool_tessellated = props.generator or props.component != None
- ob0 = props.generator
- ob1 = props.component
- except: bool_tessellated = False
- layout = self.layout
- if bool_tessellated:
- col = layout.column(align=True)
- # component XY
- row = col.row(align=True)
- row.prop(props, "mode", expand=True)
-
- if props.mode != 'BOUNDS':
- col.separator()
- row = col.row(align=True)
- row.label(text="X:")
- row.prop(
- props, "bounds_x", text="Bounds X", icon='NONE', expand=True,
- slider=False, toggle=False, icon_only=False, event=False,
- full_event=False, emboss=True, index=-1)
-
- row = col.row(align=True)
- row.label(text="Y:")
- row.prop(
- props, "bounds_y", text="Bounds X", icon='NONE', expand=True,
- slider=False, toggle=False, icon_only=False, event=False,
- full_event=False, emboss=True, index=-1)
-
-
- class TISSUE_PT_tessellate_rotation(Panel):
- bl_space_type = 'PROPERTIES'
- bl_region_type = 'WINDOW'
- bl_context = "data"
- bl_parent_id = "TISSUE_PT_tessellate_object"
- bl_label = "Rotation"
- bl_options = {'DEFAULT_CLOSED'}
-
-
- @classmethod
- def poll(cls, context):
- try:
- bool_tessellated = context.object.tissue_tessellate.generator != None
- return context.object.type == 'MESH' and bool_tessellated
- except:
- return False
-
- def draw(self, context):
- ob = context.object
- props = ob.tissue_tessellate
- allowed_obj = ('MESH','CURVE','SURFACE','FONT', 'META')
-
- try:
- bool_tessellated = props.generator or props.component != None
- ob0 = props.generator
- ob1 = props.component
- except: bool_tessellated = False
- layout = self.layout
- if bool_tessellated:
- # rotation
- layout.use_property_split = True
- layout.use_property_decorate = False # No animation.
- col = layout.column(align=True)
- col.prop(props, "rotation_mode", text='Rotation', icon='NONE', expand=False,
- slider=True, toggle=False, icon_only=False, event=False,
- full_event=False, emboss=True, index=-1)
- if props.rotation_mode == 'WEIGHT':
- col.prop(props, "rotation_direction", expand=False,
- slider=True, toggle=False, icon_only=False, event=False,
- full_event=False, emboss=True, index=-1)
- if props.rotation_mode == 'RANDOM':
- col.prop(props, "random_seed")
- else:
- col.prop(props, "rotation_shift")
-
- if props.rotation_mode == 'UV':
- uv_error = False
- if props.generator.type != 'MESH':
- row = col.row(align=True)
- row.label(
- text="UV rotation supported only for Mesh objects",
- icon='ERROR')
- uv_error = True
- else:
- if len(props.generator.data.uv_layers) == 0:
- row = col.row(align=True)
- row.label(text="'" + props.generator.name +
- " doesn't have UV Maps", icon='ERROR')
- uv_error = True
- if uv_error:
- row = col.row(align=True)
- row.label(text="Default rotation will be used instead",
- icon='INFO')
-
- class TISSUE_PT_tessellate_thickness(Panel):
- bl_space_type = 'PROPERTIES'
- bl_region_type = 'WINDOW'
- bl_context = "data"
- bl_parent_id = "TISSUE_PT_tessellate_object"
- bl_label = "Thickness"
- #bl_options = {'DEFAULT_CLOSED'}
-
- @classmethod
- def poll(cls, context):
- try:
- bool_tessellated = context.object.tissue_tessellate.generator != None
- return context.object.type == 'MESH' and bool_tessellated
- except:
- return False
-
- def draw(self, context):
- ob = context.object
- props = ob.tissue_tessellate
- allowed_obj = ('MESH','CURVE','SURFACE','FONT', 'META')
-
- try:
- bool_tessellated = props.generator or props.component != None
- ob0 = props.generator
- ob1 = props.component
- except: bool_tessellated = False
- layout = self.layout
- #layout.use_property_split = True
- if bool_tessellated:
- col = layout.column(align=True)
- # component Z
- row = col.row(align=True)
- row.prop(props, "scale_mode", expand=True)
- col.prop(props, "zscale", text="Scale", icon='NONE', expand=False,
- slider=True, toggle=False, icon_only=False, event=False,
- full_event=False, emboss=True, index=-1)
- if props.mode == 'BOUNDS':
- col.prop(props, "offset", text="Offset", icon='NONE', expand=False,
- slider=True, toggle=False, icon_only=False, event=False,
- full_event=False, emboss=True, index=-1)
-
- # Direction
- col = layout.column(align=True)
- row = col.row(align=True)
- row.label(text="Direction:")
- row = col.row(align=True)
- row.prop(
- props, "normals_mode", text="Direction", icon='NONE', expand=True,
- slider=False, toggle=False, icon_only=False, event=False,
- full_event=False, emboss=True, index=-1)
-
-
- class TISSUE_PT_tessellate_options(Panel):
- bl_space_type = 'PROPERTIES'
- bl_region_type = 'WINDOW'
- bl_context = "data"
- bl_parent_id = "TISSUE_PT_tessellate_object"
- bl_label = " "
- bl_options = {'DEFAULT_CLOSED'}
-
- @classmethod
- def poll(cls, context):
- try:
- bool_tessellated = context.object.tissue_tessellate.generator != None
- return context.object.type == 'MESH' and bool_tessellated
- except:
- return False
-
- def draw_header(self, context):
- ob = context.object
- props = ob.tissue_tessellate
- self.layout.prop(props, "merge")
-
- def draw(self, context):
- ob = context.object
- props = ob.tissue_tessellate
- allowed_obj = ('MESH','CURVE','SURFACE','FONT', 'META')
-
- try:
- bool_tessellated = props.generator or props.component != None
- ob0 = props.generator
- ob1 = props.component
- except: bool_tessellated = False
- layout = self.layout
- layout.use_property_split = True
- layout.use_property_decorate = False # No animation.
- if bool_tessellated:
- col = layout.column(align=True)
- if props.merge:
- col.prop(props, "merge_thres")
- col.prop(props, "bool_dissolve_seams")
- col.prop(props, "close_mesh")
- if props.close_mesh != 'NONE':
- #row = col.row(align=True)
- col.separator()
- col.prop(props, "open_edges_crease", text="Crease")
- col.prop(props, "cap_material_index", text='Material Index')
- if props.close_mesh == 'BRIDGE':
- col.separator()
- col.prop(props, "bridge_cuts")
- col.prop(props, "bridge_smoothness")
-
-
- class TISSUE_PT_tessellate_morphing(Panel):
- bl_space_type = 'PROPERTIES'
- bl_region_type = 'WINDOW'
- bl_context = "data"
- bl_parent_id = "TISSUE_PT_tessellate_object"
- bl_label = "Weight and Morphing"
- bl_options = {'DEFAULT_CLOSED'}
-
- @classmethod
- def poll(cls, context):
- try:
- bool_tessellated = context.object.tissue_tessellate.generator != None
- return context.object.type == 'MESH' and bool_tessellated
- except:
- return False
-
- def draw(self, context):
- ob = context.object
- props = ob.tissue_tessellate
- allowed_obj = ('MESH','CURVE','SURFACE','FONT', 'META')
-
- try:
- bool_tessellated = props.generator or props.component != None
- ob0 = props.generator
- ob1 = props.component
- except: bool_tessellated = False
- layout = self.layout
- if bool_tessellated:
- allow_shapekeys = not props.com_modifiers
- for m in ob0.data.materials:
- try:
- o = bpy.data.objects[m.name]
- allow_multi = True
- try:
- if o.data.shape_keys is None: continue
- elif len(o.data.shape_keys.key_blocks) < 2: continue
- else: allow_shapekeys = not props.com_modifiers
- except: pass
- except: pass
- col = layout.column(align=True)
- #col.label(text="Morphing:")
- row = col.row(align=True)
- col2 = row.column(align=True)
- col2.prop(props, "bool_vertex_group", icon='GROUP_VERTEX')
- #col2.prop_search(props, "vertex_group", props.generator, "vertex_groups")
- try:
- if len(props.generator.vertex_groups) == 0:
- col2.enabled = False
- except:
- col2.enabled = False
- row.separator()
- col2 = row.column(align=True)
- row2 = col2.row(align=True)
- row2.prop(props, "bool_shapekeys", text="Use Shape Keys", icon='SHAPEKEY_DATA')
- row2.enabled = allow_shapekeys
- if not allow_shapekeys:
- col2 = layout.column(align=True)
- row2 = col2.row(align=True)
- row2.label(text="Use Shape Keys is not compatible with Use Modifiers", icon='INFO')
-
-
- class TISSUE_PT_tessellate_selective(Panel):
- bl_space_type = 'PROPERTIES'
- bl_region_type = 'WINDOW'
- bl_context = "data"
- bl_parent_id = "TISSUE_PT_tessellate_object"
- bl_label = "Selective"
- bl_options = {'DEFAULT_CLOSED'}
-
- @classmethod
- def poll(cls, context):
- try:
- bool_tessellated = context.object.tissue_tessellate.generator != None
- return context.object.type == 'MESH' and bool_tessellated
- except:
- return False
-
- def draw(self, context):
- ob = context.object
- props = ob.tissue_tessellate
- allowed_obj = ('MESH','CURVE','SURFACE','FONT', 'META')
-
- try:
- bool_tessellated = props.generator or props.component != None
- ob0 = props.generator
- ob1 = props.component
- except: bool_tessellated = False
- layout = self.layout
- if bool_tessellated:
- allow_multi = False
- allow_shapekeys = not props.com_modifiers
- for m in ob0.data.materials:
- try:
- o = bpy.data.objects[m.name]
- allow_multi = True
- try:
- if o.data.shape_keys is None: continue
- elif len(o.data.shape_keys.key_blocks) < 2: continue
- else: allow_shapekeys = not props.com_modifiers
- except: pass
- except: pass
- # LIMITED TESSELLATION
- col = layout.column(align=True)
- #col.label(text="Limited Tessellation:")
- row = col.row(align=True)
- col2 = row.column(align=True)
- col2.prop(props, "bool_selection", text="On selected Faces", icon='RESTRICT_SELECT_OFF')
- row.separator()
- if props.generator.type != 'MESH':
- col2.enabled = False
- col2 = row.column(align=True)
- col2.prop(props, "bool_material_id", icon='MATERIAL_DATA', text="Material ID")
- if props.bool_material_id and not props.bool_multi_components:
- #col2 = row.column(align=True)
- col2.prop(props, "material_id")
- if props.bool_multi_components:
- col2.enabled = False
-
- col.separator()
- row = col.row(align=True)
- col2 = row.column(align=True)
- col2.prop(props, "bool_multi_components", icon='MOD_TINT')
- if not allow_multi:
- col2.enabled = False
-
-
- class TISSUE_PT_tessellate_iterations(Panel):
- bl_space_type = 'PROPERTIES'
- bl_region_type = 'WINDOW'
- bl_context = "data"
- bl_parent_id = "TISSUE_PT_tessellate_object"
- bl_label = "Iterations"
- bl_options = {'DEFAULT_CLOSED'}
-
- @classmethod
- def poll(cls, context):
- try:
- bool_tessellated = context.object.tissue_tessellate.generator != None
- return context.object.type == 'MESH' and bool_tessellated
- except:
- return False
-
- def draw(self, context):
- ob = context.object
- props = ob.tissue_tessellate
- allowed_obj = ('MESH','CURVE','SURFACE','FONT', 'META')
-
- try:
- bool_tessellated = props.generator or props.component != None
- ob0 = props.generator
- ob1 = props.component
- except: bool_tessellated = False
- layout = self.layout
- layout.use_property_split = True
- layout.use_property_decorate = False # No animation.
- if bool_tessellated:
- col = layout.column(align=True)
- row = col.row(align=True)
- #row.label(text='', icon='FILE_REFRESH')
- col.prop(props, 'iterations', text='Repeat')#, icon='FILE_REFRESH')
- if props.iterations > 1 and props.fill_mode == 'PATCH':
- col.separator()
- #row = col.row(align=True)
- col.prop(props, 'patch_subs')
- layout.use_property_split = False
- col = layout.column(align=True)
- #row = col.row(align=True)
- col.label(text='Combine Iterations:')
- row = col.row(align=True)
- row.prop(
- props, "combine_mode", text="Combine:",icon='NONE', expand=True,
- slider=False, toggle=False, icon_only=False, event=False,
- full_event=False, emboss=True, index=-1)
-
- class tissue_rotate_face_right(Operator):
- bl_idname = "mesh.tissue_rotate_face_right"
- bl_label = "Rotate Faces Right"
- bl_description = "Rotate clockwise selected faces and update tessellated meshes"
- bl_options = {'REGISTER', 'UNDO'}
-
- @classmethod
- def poll(cls, context):
- try:
- #bool_tessellated = context.object.tissue_tessellate.generator != None
- ob = context.object
- return ob.type == 'MESH' and ob.mode == 'EDIT'# and bool_tessellated
- except:
- return False
-
- def execute(self, context):
- ob = context.active_object
- me = ob.data
-
- bm = bmesh.from_edit_mesh(me)
- mesh_select_mode = [sm for sm in context.tool_settings.mesh_select_mode]
-
- for face in bm.faces:
- if (face.select):
- vs = face.verts[:]
- vs2 = vs[-1:]+vs[:-1]
- material_index = face.material_index
- bm.faces.remove(face)
- f2 = bm.faces.new(vs2)
- f2.select = True
- f2.material_index = material_index
- bm.normal_update()
-
- # trigger UI update
- bmesh.update_edit_mesh(me)
- ob.select_set(False)
-
- # update tessellated meshes
- bpy.ops.object.mode_set(mode='OBJECT')
- for o in [obj for obj in bpy.data.objects if
- obj.tissue_tessellate.generator == ob and obj.visible_get()]:
- context.view_layer.objects.active = o
- bpy.ops.object.tissue_update_tessellate()
- o.select_set(False)
- ob.select_set(True)
- context.view_layer.objects.active = ob
- bpy.ops.object.mode_set(mode='EDIT')
- context.tool_settings.mesh_select_mode = mesh_select_mode
-
- return {'FINISHED'}
-
- class tissue_rotate_face_left(Operator):
- bl_idname = "mesh.tissue_rotate_face_left"
- bl_label = "Rotate Faces Left"
- bl_description = "Rotate counterclockwise selected faces and update tessellated meshes"
- bl_options = {'REGISTER', 'UNDO'}
-
- @classmethod
- def poll(cls, context):
- try:
- #bool_tessellated = context.object.tissue_tessellate.generator != None
- ob = context.object
- return ob.type == 'MESH' and ob.mode == 'EDIT'# and bool_tessellated
- except:
- return False
-
- def execute(self, context):
- ob = context.active_object
- me = ob.data
-
- bm = bmesh.from_edit_mesh(me)
- mesh_select_mode = [sm for sm in context.tool_settings.mesh_select_mode]
-
- for face in bm.faces:
- if (face.select):
- vs = face.verts[:]
- vs2 = vs[1:]+vs[:1]
- material_index = face.material_index
- bm.faces.remove(face)
- f2 = bm.faces.new(vs2)
- f2.select = True
- f2.material_index = material_index
- bm.normal_update()
-
- # trigger UI update
- bmesh.update_edit_mesh(me)
- ob.select_set(False)
-
- # update tessellated meshes
- bpy.ops.object.mode_set(mode='OBJECT')
- for o in [obj for obj in bpy.data.objects if
- obj.tissue_tessellate.generator == ob and obj.visible_get()]:
- context.view_layer.objects.active = o
- bpy.ops.object.tissue_update_tessellate()
- o.select_set(False)
- ob.select_set(True)
- context.view_layer.objects.active = ob
- bpy.ops.object.mode_set(mode='EDIT')
- context.tool_settings.mesh_select_mode = mesh_select_mode
-
- return {'FINISHED'}
-
-
- def convert_to_frame(ob, props, use_modifiers):
- new_ob = convert_object_to_mesh(ob, use_modifiers, True)
-
- # create bmesh
- bm = bmesh.new()
- bm.from_mesh(new_ob.data)
- bm.verts.ensure_lookup_table()
- bm.edges.ensure_lookup_table()
- bm.faces.ensure_lookup_table()
- if props.bool_selection:
- original_faces = [f for f in bm.faces if f.select]
- else:
- original_faces = list(bm.faces)
- # detect edge loops
-
- loops = []
- boundaries_mat = []
- neigh_face_center = []
- face_normals = []
- # append boundary loops
- if props.frame_boundary:
- #selected_edges = [e for e in bm.edges if e.select]
- selected_edges = [e for e in bm.edges if e.is_boundary]
- if len(selected_edges) > 0:
- loop = []
- count = 0
- e0 = selected_edges[0]
- face = e0.link_faces[0]
- boundary_mat = [face.material_index]
- face_center = [face.calc_center_median()]
- loop_normals = [face.normal]
- selected_edges = selected_edges[1:]
- if props.bool_vertex_group:
- n_verts = len(new_ob.data.vertices)
- base_vg = [get_weight(vg,n_verts) for vg in new_ob.vertex_groups]
- '''
- base_vg = []
- for vg in new_ob.vertex_groups:
- vertex_group = []
- for v in bm.verts:
- try:
- vertex_group.append(vg.weight(v.index))
- except:
- vertex_group.append(0)
- base_vg.append(vertex_group)
- '''
- while True:
- new_vert = None
- face = None
- for e1 in selected_edges:
- if e1.verts[0] in e0.verts: new_vert = e1.verts[1]
- elif e1.verts[1] in e0.verts: new_vert = e1.verts[0]
- if new_vert != None:
- if len(loop)==0:
- loop = [v for v in e1.verts if v != new_vert]
- loop.append(new_vert)
- e0 = e1
- face = e0.link_faces[0]
- boundary_mat.append(face.material_index)
- face_center.append(face.calc_center_median())
- loop_normals.append(face.normal)
- selected_edges.remove(e0)
- break
- if new_vert == None:
- try:
- loops.append(loop)
- loop = []
- e0 = selected_edges[0]
- selected_edges = selected_edges[1:]
- boundaries_mat.append(boundary_mat)
- neigh_face_center.append(face_center)
- face_normals.append(loop_normals)
- face = e0.link_faces[0]
- boundary_mat = [face.material_index]
- face_center = [face.calc_center_median()]
- loop_normals = [face.normal]
- except: break
- boundaries_mat.append(boundary_mat)
- neigh_face_center.append(face_center)
- face_normals.append(loop_normals)
- # compute boundary frames
- new_faces = []
- vert_ids = []
-
- # append regular faces
- for f in original_faces:#bm.faces:
- loop = list(f.verts)
- loops.append(loop)
- boundaries_mat.append([f.material_index for v in loop])
- face_normals.append([f.normal for v in loop])
-
- # calc areas for relative frame mode
- if props.frame_mode == 'RELATIVE':
- verts_area = []
- for v in bm.verts:
- linked_faces = v.link_faces
- if len(linked_faces) > 0:
- area = sum([sqrt(f.calc_area())/len(f.verts) for f in v.link_faces])*2
- area /= len(linked_faces)
- else: area = 0
- verts_area.append(area)
-
- for loop_index, loop in enumerate(loops):
- is_boundary = loop_index < len(neigh_face_center)
- materials = boundaries_mat[loop_index]
- new_loop = []
- loop_ext = [loop[-1]] + loop + [loop[0]]
-
- # calc tangents
- tangents = []
- for i in range(len(loop)):
- # vertices
- vert0 = loop_ext[i]
- vert = loop_ext[i+1]
- vert1 = loop_ext[i+2]
- # edge vectors
- vec0 = (vert0.co - vert.co).normalized()
- vec1 = (vert.co - vert1.co).normalized()
- # tangent
- _vec1 = -vec1
- _vec0 = -vec0
- ang = (pi - vec0.angle(vec1))/2
- normal = face_normals[loop_index][i]
- tan0 = normal.cross(vec0)
- tan1 = normal.cross(vec1)
- tangent = (tan0 + tan1).normalized()/sin(ang)*props.frame_thickness
- tangents.append(tangent)
-
- # calc correct direction for boundaries
- mult = -1
- if is_boundary:
- dir_val = 0
- for i in range(len(loop)):
- surf_point = neigh_face_center[loop_index][i]
- tangent = tangents[i]
- vert = loop_ext[i+1]
- dir_val += tangent.dot(vert.co - surf_point)
- if dir_val > 0: mult = 1
-
- # add vertices
- for i in range(len(loop)):
- vert = loop_ext[i+1]
- if props.frame_mode == 'RELATIVE': area = verts_area[vert.index]
- else: area = 1
- new_co = vert.co + tangents[i] * mult * area
- # add vertex
- new_vert = bm.verts.new(new_co)
- new_loop.append(new_vert)
- vert_ids.append(vert.index)
- new_loop.append(new_loop[0])
-
- # add faces
- materials += [materials[0]]
- for i in range(len(loop)):
- v0 = loop_ext[i+1]
- v1 = loop_ext[i+2]
- v2 = new_loop[i+1]
- v3 = new_loop[i]
- face_verts = [v1,v0,v3,v2]
- if mult == -1: face_verts = [v0,v1,v2,v3]
- new_face = bm.faces.new(face_verts)
- new_face.material_index = materials[i+1] + props.frame_boundary_mat
- new_face.select = True
- new_faces.append(new_face)
- # fill frame
- if props.fill_frame and not is_boundary:
- n_verts = len(new_loop)-1
- loop_center = Vector((0,0,0))
- for v in new_loop[1:]: loop_center += v.co
- loop_center /= n_verts
- center = bm.verts.new(loop_center)
- for i in range(n_verts):
- v0 = new_loop[i+1]
- v1 = new_loop[i]
- face_verts = [v1,v0,center]
- new_face = bm.faces.new(face_verts)
- new_face.material_index = materials[i] + props.frame_boundary_mat
- new_face.select = True
- new_faces.append(new_face)
- bpy.ops.object.mode_set(mode='OBJECT')
- #for f in bm.faces: f.select_set(f not in new_faces)
- for f in original_faces: bm.faces.remove(f)
- bm.to_mesh(new_ob.data)
- # propagate vertex groups
- if props.bool_vertex_group:
- base_vg = []
- for vg in new_ob.vertex_groups:
- vertex_group = []
- for v in bm.verts:
- try:
- vertex_group.append(vg.weight(v.index))
- except:
- vertex_group.append(0)
- base_vg.append(vertex_group)
- new_vert_ids = range(len(bm.verts)-len(vert_ids),len(bm.verts))
- for vg_id, vg in enumerate(new_ob.vertex_groups):
- for ii, jj in zip(vert_ids, new_vert_ids):
- vg.add([jj], base_vg[vg_id][ii], 'REPLACE')
- new_ob.data.update()
- return new_ob
-
- def convert_to_fan(ob, props, use_modifiers):
- new_ob = convert_object_to_mesh(ob, use_modifiers, True)
- # make base object selected and active
- for o in bpy.context.view_layer.objects: o.select_set(False)
- new_ob.select_set(True)
- bpy.context.view_layer.objects.active = new_ob
- sk_index0 = new_ob.active_shape_key_index
- new_ob.active_shape_key_index = 0
-
- bpy.ops.object.mode_set(mode='EDIT')
- bpy.ops.mesh.select_mode(type='FACE')
- if not props.bool_selection:
- bpy.ops.mesh.select_all(action='SELECT')
- bpy.ops.mesh.poke()
- bpy.ops.object.mode_set(mode='OBJECT')
- return new_ob
|