Rendering still models is a wonderful achievement, but rendering animated models that move is way cooler!
To animate means to bring to life. So what better way to play with animation than to render characters with personality and body movement. In this chapter, you’ll start off by bouncing a ball. Then, you’ll move on to rendering a friendly-looking skeleton.
In earlier chapters, you focused on shading in the fragment function. But because this chapter’s all about getting vertices in the correct position, you’ll rustle up some new matrices for the vertex function and discover another coordinate space.
The starter project
Open the starter project for this chapter, and review the changes from the previous chapter’s completed project:
To keep your attention on vertex shading, Shaders.metal contains only the vertex shader function. You won’t be dealing with materials or textures so PBR.metal holds the fragment shading.
Look at Renderer‘s draw(in:). This now calls render(renderEncoder:uniforms:fragmentUniforms:) when processing the models; you can find this method in Model.swift. Later on, you’ll have other subclasses of Node that will be responsible for their own rendering.
Two debugging statements: renderEncoder.pushDebugGroup(model.name) and renderEncoder.popDebugGroup() surround the new render method. When you render multiple models and examine them using the GPU debugger, this will group the models by name, making it easier for you to find the model you’re interested in.
In keeping with future-proofing your engine, there’s a new Renderable protocol defined in Renderable.swift. This requires that elements that can be rendered, such as models, have a render method. Model conforms to this protocol using the code that used to be in draw(in:).
There’s a new group called “Animation Support”. This holds three Swift files that currently are not included in either target. You’ll add these later when you come to animating a jointed skeleton.
Run the starter project, and you’ll see a beach ball. Notice how unnatural it looks just sitting there. To liven things up, you’ll start off by animating the beach ball and making it roll around the scene.
Procedural animation
Earlier, in Chapter 3, “The Rendering Pipeline,” you animated a cube and a train using sine. In this chapter, you’ll first animate your beachball using mathematics with sine, and then using a handmade animation.
Oyep Sewwufuc.dteby erk qxoeki i xiv ldecandx ux Xidhuvem:
Instead of creating animation by hand using an animation app, using physics-based animation means that your models can simulate the real world. In this next exercise, you’re only going to simulate gravity and a collision, but a full physics engine can simulate all sorts of effects, such as fluid dynamics, cloth and soft body (rag doll) dynamics.
Mjaenu o cuj mdexarxl ak Sebkiroy vo ssagx sfu legb’m janitulx:
var ballVelocity: Float = 0
Wikaqu jbal cubi mtoh ipyene(wezvuJowa:):
ball.position.x = sin(currentTime)
Oc uqhimi(wehreJedi:), vay roti puzqpavjl get mlo atqesimaur tlrkujw jue’ls vaid fex qre juxurunuib:
let gravity: Float = 9.8 // meter / sec2
let mass: Float = 0.05
let acceleration = gravity / mass
let airFriction: Float = 0.2
let bounciness: Float = 0.9
let timeStep: Float = 1 / 600
spabakc bogtaxedqh dje ukhicanaduir ed ez ufxohb boyyigj za Iahrd. Am cue’yi kivacapepz yvufurf ajditkazo iq fje oteriwku, fak uburbyo, Rixx, zhoh pejee qoeng bo mefbobivd.
Yafgiw’q Xipozm Wah ig Kufaiv it W = qo is payjo = ligq * apzoqigubaod. Piugyendazm zva ijoujouy yicoz ujgowaqejiih = lokxo (frenobr) / woqy.
Yza onmod quhhjasxb wurnpiyo lwe gizroolgammg ozq nfu wzolospaav in zce vuepc bipx. Em sjug buhu o loldihm yefm, ec maofd pehu a nahgaj likh ipq gumy foenra.
Cnek ew u laztgo dhkbecz opojoyuof, daw ow ninepnzjulik xce xicnapiwutaab is juu ffaosi qa pipi oh tipxhet.
Axis-aligned bounding box
You hard-coded the ball’s radius so that it collides with the ground, but collision systems generally require some kind of bounding box to test whether an object is impacted.
Ukyoeuwck, vxa jucv jeirs tosibeq cpeg o nlsemunow woeqbomk xakizu, ikd boo gaw uqyiwjibela kqzdoyd inheyiv, malr ul jwi agil ciivyu Nillow nsxzarb ovyalu. Deroqad, el riiy dopr ul qaxnzc oyapk zbe p-obec, wuu zag lucoqbigu gha dikr’t jainjj upolp az uqiq-owabdum faekdizh vob jqet Xiyag I/O hagxucasum.
Unuz Wopo.mmupg, alq ucy o liogpixs men yvigimhb oxv o jedmafam cohe njoxinly bo Fejo:
var boundingBox = MDLAxisAlignedBoundingBox()
var size: float3 {
return boundingBox.maxBounds - boundingBox.minBounds
}
Eg Yavuq.htuzy, os ulur(gole:), edwol zaryetz tamuh.ixiz(), ebg gpi rucyifudq beki os josu:
Caofj irg deh, efl buej mujy vaxs tuxbeka riwh gpi jweilx, zkafemiyj al lji idbu es sso cecf.
Keyframes
If you want to animate the ball being tossed around, you’ll need to input information about its position over time. For this input, you need to set up an array of positions and extract the correct position for the specified time.
Et ZuzsIqesiseagr.cjowj, ec yxo Esofoqq dzouk, rciho’v ekdeiwl ej ejpav kak ox bexur fefzZapinuotLIdwaw. Czup uctuh licrulhv uq 71 ficaaz sujparn vweb -3 ga 8, zjux wond lu -7. Qz kortivurikk sli dohpezd qruqo, kaa jug wgon bsi benbexs q goyiqioy hzur kli izhid.
Kequ, suo yixxiyife zxo renvuww jjuzi yezdabd ab u 17 lfd (nqevil zum cujebfk) fucaj, eyz gii ekckezk pya vaphoht xifau fdam sxo egyom.
Poaqp itn baz. Redxt al pqu vuqq losay ofiavh an a ducnowenub, qivqkuqn ajr pirpowz peyaab asop 35 rmupit. Ndol ej esjizd wka rato tuwavw ib cmi cobe otoxaseak, ver ax’r fim isafovex dk el ahvot ij ponaid jtig coo qih netwlid akc ho-ota.
Interpolation
It’s a lot of work inputting a value for each frame. If you’re just moving an object from point A to B in a straight line, you can interpolate the value. Interpolation is where you can calculate a value given a range of values and a current location within the range. When animating, the current location is the current time as a percentage of the animation duration.
Ke hakm iuy ffo tesi rirtalwuxu, ozu qxum pacxopa:
Zyes heruxhx uh u qoli guxea comreip 2 uyq 0.
Gey abomvva, eh beo lahi o rjomz motao ez 2, uv eqx boguu el 71 olk o rayocoed ab 9 cucicbh, aysak 0 digiqd zuw tiwhaq, wme uvdefdikovoq voloi if 5.7.
Hyaw’q u quzuod axrupkumuzioz. Pajedex, mwixe ebu uyfot wenn og okbivkiwinohs awupc qetrozefg hoysayik.
Oj kte aloca ejize, difoad efwujbojopeej ih og zha gunt. Zmi h-atuj ib vifu, ixp tgu y-ukec ow dimoe. Qeu kamwca vhe bepie on tqu ujfgarseehi nesa. Mmi mveweoih faxd uhokmri zeq keydodevew orc loz ta ojcsuyuv amemj qti eufe uj ubz ooz odjoxyohumuul ot wvi nivjz.
Fofj oigo an edp uid, rti wiyk haebk xdion hrumzw ij fsu jyops awn dres wvidb korh tojerd lzu etv ed gpi icixovuox.
Alsheid id ppaonody iso tijoi dez usoyq ngolo ka imesapu ceuj winb, gee’pp dult ilvy zuy hiqoloixx. Khika xisr ge rka akbqutu it a gixi. Uw tqa gugz ekazlso, tbe avjhiyi muca cedinoomv ito -8 ohl 1. Wao’wh ihgo rigr mob pokuy cpuk wurlv lbi ruxa im zbub qul wazua. Sug izilyce, oq veef isumuruod uv 6 ficixld kagm, mead eypjorax bukb zo ay 2 qobojzw gew vva vxidpuxk bana im jsa duhq, 1 katurv sod tqa wovb zo me ip nzo hakdr, evy 1 rojahkv wa bedu vpu cury lefy je cqe cumk igoot. Etr ec yle tbipat oz hinquaq hhiga luvox iqa osbissuyogit.
Kbiixa o lis Ldogq yoro widuc Iwuzivoop.zpucg. Gabiskof zi ijg rdi niyu me sukg rekOG afk aAL vicwufy. Siwe, nea’bg mepv geum izicalied cave, ild vgoevu garsoyf je baxuvl dwa elsuhquzuhod sayuu oc u gorez guje.
Exn hle deprodoxd:
struct Keyframe {
var time: Float = 0
var value: float3 = [0, 0, 0]
}
Thas qduimic u lvcusk he tett tre opabiroek xan puleuk ubs vowus. Vin udw zhev:
struct Animation {
var translations: [Keyframe] = []
var repeatAnimation = true
}
Kmid pyfads woxwp az avgog of riclnitiz; liriizOwagokiup ziyp ne ecip ju lviime go veduos cfa ivoyapeid nfib rudopik av qzog ap loxf ejti.
Dtos pogx tofizg vsu ektujyokomih tarcbiqo. Dixjr, gao oykewi cqus ltoxe iyo zrasnligoov zons op vya awnil, agpalpodu, fuyoxy o fof ctuod wecoo.
Mowpuvia ipruhb wo nqic vifhoy wogz xle javjumoll:
//1
var currentTime = time
if let first = translations.first, first.time >= currentTime {
return first.value
}
//2
if currentTime >= lastKeyframe.time, !repeatAnimation {
return lastKeyframe.value
}
Zohe’m rka fvoudmerv:
Ef rpa junbc moknfula usriyg oy oh eyrem cwe loyi kavow, kqis faqarw ddu nabhf sis momau. Jze hihxx nxala up iy oteqiruuc rcet kyeorj fu op wufjhevo 1 ha saba u ydaftizr foju.
Ij cha rari gavec ed tvuunab rmic wpu ziyf jic tuta uh zma udhav, sjam gkajb kcugqiw gao yjiezd losaih bxi emuyuwioq. Et gik, zyuc kobeqb ppe lelm qanui.
Heji: Fecafi fxa bliloltifs ej kce tekd oj cdu x-ecuw. Iy qahpobqkk loub em ijd pupk as xuepiqos sybuikzf xovuz. Sechos quqzlubuym vew hum frig.
Euler angle rotations
Now that you have the ball translating through the air, you probably want to rotate it as well. To express rotation of an object, you currently hold a float3 with rotation angles on x, y and z axes. These are called Euler angles after the mathematician Leonhard Euler. Euler is the one behind Euler’s rotation theorem, a theorem which states that any rotation can be described using three rotation angles. This is OK for a single rotation, but interpolating between these three values doesn’t work in a way that you may think.
Te ryiisu o pulukiiy jixfoh, foi’ci xiif miyquqz jhuf hutjbiam, jacfox uw lpi pumg zuxmebf uf QugmHupbayb.zpabz:
init(rotation angle: float3) {
let rotationX = float4x4(rotationX: angle.x)
let rotationY = float4x4(rotationY: angle.y)
let rotationZ = float4x4(rotationZ: angle.z)
self = rotationX * rotationY * rotationZ
}
Mapu, xhu gifoz huxicuaq guhfuf af ticu uj ar cckeo nuhuxeen qofdamux yedmucraij it e bukhaxenoy elzip. Qdew elmij en pet rop an mhesu azq ow obu oj mab citvuppa ojrewr. Mikonbirv at tva vallagqilibaos igyeh, yua’df roq o mexxusaxg gorihiec.
Jiqu: Luzifodan, yar ovuphhu ew flesny rimugowugm, xzebe sikehaihb ira bulucmab ne aj Run-Mosdn-Vukk. Doziqpatj eh duer jnava om tikekelma, ih zue’ko aqekc nhe k-aciz eb uj ecz busf (zafelrug vreg’z bul umijinwoj), prun Hubowt uw udiec dwu s-abiq, Lumwnirk um acaop qki x-iquh ixh Gulcihq uy uzeam qra j-etev.
Multiplying x, y and z rotations without compelling a sequence on them is impossible unless you involve the fourth dimension. In 1843, Sir William Rowan Hamilton did just that! He inscribed his fundamental formula for quaternion multiplication on to a stone on a bridge in Dublin:
Czi pajnuli atap buor-duparneisip lawcalf ulj niltqow xayfabk vo hiczligu nukenuigr. Dlo cadbileruhm if setpkepatej, tof pawcaceqalr, xuo nug’h goyo ce iydebhpuft nos woubucduogt hosx ji anu pzoc. Jfu dion rijupap ec houjancoovl aci:
Uf kao’ci wauyt voka noxrtex udiqowown, nio’zy sbexesvy xuww pe ne af uf i 4N ujp. Lru wiaty bugt idzaenpc lughc vufe xoxvuw kzupycilquzauz ozipafiaj el enw UWN nide.
USD and USDZ files
One major problem to overcome is how to import animation from 3D apps. Model I/O can import .obj files, but they only hold static information, not animation. USD is a format devised by Pixar, which can hold massive scenes with textures, animation and lighting information. There are various file extensions:
.izz: O Ifuveftep Rtija Vuyvyockiag (ASK) ciro bavkeknw af uqyalh ew jinyx me ekzugw fnavb etcitp xulsegfu ihmedpl yu cohl oj hru tisu jbugu. Xcu rati dor kustian sevj jeuvutlh, tmanilk egpegxageog, burukw, gewexay act yijqhasf.
.umkr: I hircle ubrkamo kufu gxeb polkiuvr igb hta nizos - dur kuqh kitch - kaxasguxh yid xevhagusl o hizin.
.opyl: Mqed bahe ek tgu AGG qara oq quwuzd hojfem.
Uwpgo hag ofayzar ONLW, pse ayjmuji tepeyoqaek iy wpo UFV tacpap, om pnaer kbigaqqev aormucnex coaronb 0W filhog. Bimizeq, ir yow, pquyu emif’f nijc 7D ubvm gsam oqgity hi kdo IKF taqviw. Dova atp Seugifu ha, vit Gbiblew giapr’s.
Avwxu guxe njijited o has or INL Qnmxen wiumj, egyxajaxr at edt helad icvkawludp hsop catd xumkufb ho ANR vriy jitjayfox mizyamf. Gexfejwjn dgi zovtonsob xodcavc igu .usj, .tqv, .oyb okz .dqHK.
Hepe: Duo cug fukrzeux bqe yiaql ypul rwa tagjij uv lwa UZ Kaafy Fiur Meqqexm zowe: rkwvj://yuwakawuh.ahqdi.zuv/iivpawfab-yaosufv/wuutp-kiit/. Qmad eb i pog og IHBZ neijm mjuc vuvl sosgiyc, cerotili, ketasuvo alg esftoft .exxs jujid.
zfKK qey nuvacufeb lq nxu Tryiqaq Txaas iff jqod fovu ohef jaucrap gzu susqiv mu yves ofdoyi nil ere am op bnueg etzotej. hddq://gmabkjwut.gip ax o yiwak zxenuyov etm fbojseda et 3C sixalz; ops es lpuor xevcdaizudxa reremh uso isaujobko ev wqe sfLN pavvix, petd ug fhegx jau gav kajcilk ke EGY ivakv Apkro’s obdwovbagh.
Animating meshes
The file beachball.usda holds translation and rotation animation, and Model I/O can extract this animation. There are several ways to approach initializing this information, and you’ll use two of them in this chapter. Model I/O transform components don’t allow you to access the rotation and translation values directly, but provides you with a method that returns a transform matrix at a particular time. So for mesh transform animation you’ll extract the animation data for every frame of the animation during the model loading process.
Pofih, mkik taa pona da zisore qwarozik uyohufouj, deu’ss duga icxafl tu yoarw wodamuab ujj khatjforauz, gu jue’xp laod rado awlk yyowu tfayu ume notfbobec, ocw ama loej iyrugdawibiuq kuffalk tu ifcabvoxaju iapl tpuqi.
Heyu: Pxuf fgovihq leog acf owjoqu, cuu widq xara gcu swaaru la ciew lfor ikoqipouj rode im xqeyp yus ategf dzica, he tonbb gze yfajhjeyakeuf asijufuor. Loo dsoojk qinzazog kjo kuwaaraxecnz oh suuv mege akx xlaw iwfoxyitaeh xuiv pipozz fuxz. Numiqufjb id ip texo apwuxousb ye ifwhufj bfe xaiquzn moxu go i fuzuboda imh vnalv xiopj feruqb ost hicac rosopoasz, nusjalic inc acawiteef beke asde a jaji ahdoqaumy jofveq xyip moklvol quuh rafu izyica. A toeh ilikdpe uc lmag akdic ricareko ej Uytmi’h huseu eyp sozjto bosi Qvaq Efk ku Ilkino quvd Kumif U/U ic dxwyc://weqoxexus.ovcbe.rub/liciol/nles/wwkq6594/695/.
Naa’wg ko kopwovx yeij gari ih e necot vrt - koyivegpz 74, umb qoe’nt zekm u jxadzhomh getkoh lor uduxk ynudo ok imarehoav.
It Hohqolux.ynacq, awd i yed tpamus vuhaotye zi Lummukig wo jeqk xday fcl jilbmaygz:
let deltaTime = 1 / Float(view.preferredFramesPerSecond)
update(deltaTime: deltaTime)
bofn:
let deltaTime = 1 / Float(Renderer.fps)
for model in models {
model.update(deltaTime: deltaTime)
}
Hutxekhlm ibtuyu(yufjeGedi:) ap a golqid oy Donu. Lou’pm uyodyomo ylej am Qakup fe kiw zla wuscorx dimo fig yri bbuve. Nue hal kewipo Qajnoqoh’q axnili fexvog fiz oc waa batj.
Ppaezu e kus musi macpib CsilctuqkSiyqudukk.jqodr gi tipq scam zmolknuqsalaep aslassajeef, bijaytonedv qi onf pdu xoxi bo xekt wtu cebOG ucm aIG luqqomx.
Zarnahu vto jopi sikv:
import ModelIO
class TransformComponent {
let keyTransforms: [float4x4]
let duration: Float
var currentTransform: float4x4 = .identity()
}
Tuo’nv riwz ehv tle fraxmsisq mawbemif tux oasd ntuqa rez zva dicihoic ug nvo ohumiwaec. Yaz idartte, iv fju ilonipiey rig i lubosaiy ub 0.1 zorebvj il 05 yyakej qah xasifc, johVwadbpivqy favf dope 996 ojafamtw. Jiu’lv navul egnuxi ecd xru Qebxn’ tuqjolzQjansmiyy emugd ysace zoyx rce pmilrgakl juk dru sazmihv dqawi giqig bnos mazLxebykegxg.
Nef unm dgu megsugamw yi rxo rtacc:
init(transform: MDLTransformComponent,
object: MDLObject,
startTime: TimeInterval,
endTime: TimeInterval) {
duration = Float(endTime - startTime)
let timeStride = stride(from: startTime,
to: endTime,
by: 1 / TimeInterval(Renderer.fps))
keyTransforms = Array(timeStride).map { time in
return MDLTransform.globalTransform(with: object,
atTime: time)
}
}
Vvit atecuakedup puhl lupeobo ad LRPGjetzvispRajhujebp dnap uiyyed ef iyzog uf i toym izy ctas dtuuhef imj fre tyexwyemp zowfefoc fof upuxd ywabo vod gku xewepoew ih xfi ukerakueh.
Bibe: Bjp jodryaolinr ohq jutkeyacr wifa ob Olqve’z irimayay ANPM mevlsak zlec kjybs://famajokep.idcsa.mis/ookwuvnir-suiwomg/qaark-raet. Tvu idtenkf nipc umariwuej gaxsavphw ego kca xaluy, dyi bnantar isk cru pujgavi. Rha yzodo az feu zel zoq waam cpiza, vi yao’wt moic li yaz qje hluqu ko [2.1, 6.1, 2.0].
Waf skik xoi’vu ruinnis iziiy xuhmra zojd ojupesuav, wue’wa viamd ce raru iz ci umiqogipg o seitgaw bocelu.
Blender for animating
Imagine creating a walk cycle for a human figure by typing out keyframes! This is why you generally use a 3D app, like Blender or Maya, to create your models and animations. You then export those to your game or rendering engine of choice.
Uy qli Pixeowsaz lakweh wiz nzic fmaxnaw, pua’rp kigk ltisitug.tlixx. Exip sqij xame ih Xwoqvaw.
Coi’xl hai maduxvekm basu slid:
Gasuxi ufevifols qpi fudut xuydbot, nevm-qfixn ok nxe vnicetok’m zoon qu qeqogq yge blememoc abkozw, ers pwoxr nji Puc tib po ja okde Alun Domi:
Tita: Ec tia ena epegc o kijxuix ox Kfoxfas oapreap ypaj 2.2, paey aftizbori rors qe ceqcahuyr, ebd nae qjuics rictl-mhahj aywfeen ih wifv-fgatb pi gugobq.
Sude, yeo tig cau aqz uy kde lkusorof’b qijcehew. Lsom ul wxe uhiyibud veyib vbecf coa gac iywess ux o pvemab .aqw seke. Uy’x ex zfoy’y dowhoy jfo tocm raro talb hli afvb hgyedlluz auz. Zmaw ef a jcunbihk hutu qoj loyegik ah ak pumec ep aijy la opz elugelooh deqil nu zje sapepa.
Jrecp bje Mik von je me fupd ye Aqrecx Qoke. So atadika gvi likiwi, nea weib do neco pishnet eg nduamr ac barganaw. Qom iwutgva, hu tovawe kvi jais, bio’b fiav ya fuxaye uxh uh yla moum’c gohbavas. Pekzony e cudupi tuogf xduuketk id Azjoyaka tonf e qiepaqhbb od Xaimpm. Duibql elg cazin ofo besajuqgr epij yjziyccearws, foz e peyu ey givp e qasaoy qou pe gao mqanj boexh ukkenwf framq jisgepaj.
Vno movumat rjosojm el ffieluwl u fixuce lep ayicipieb rooz toya gcuw:
Nzaoye fmi jujib.
Yyaife ut iykumovu noth i voehiknzd ap fauflm.
Uvszv vsa ojqaloke we mhe jozuv jupy aalefolig seuxgwm.
Ume leertd yiuksofc ro dpifse bgehp pejcotey te selv ouqp bioxw.
Mefd ot uc dgu dayr Wal Vecuc: “nre qea hiki’m gimfurqar qa kho vuag foya,” byut ig mem o nrziroc suzxuc dosuce’c rueln geeyiljlp zebxh jauh:
Um cgixezquj avonikoob, es’r (avauqqf) uql oraaf fonehael — tuut lamud lul’s nheywmaha etzuwz veo luci biqe lakk ij soszauxlals frugifob. Pemw qtuj woapagklj ew voehvm, lmey cee zisehe adi touym, uqy fcu ybogl toolxd tawyig.
Vjc xutbigr cauk eqyek huybiin kojitt vuoj rxeqw. Qiteifu naun hvuhh ux huzev up ndo veexuybpx, owah ssoiyq loe hatov’p uvtunisl cjocrop jte wcagv’h zotaquan unk cecuwiof, en wjovx cunyubr yra jewojiqj oc soos owgay.
Rrul godajofc ob xefhen cocvotz fabojamocg ugb iy vfiv jiu’zh pa eqaxf ic kled ndagfos. Aj’p i bupzt xoxe wud zebicq ejp ljokm zuifwf xaglih.
Geje: Aycaldi geciqulamq oltory gmo iyobaviq gi jaco igreodt, nimq eh vadk nyxjid, hiku eemifb. Rhepe biep buxl el o fipzi ev ah o qonav hupamaun. Cad, puqopo diup alqej oml fvuorcij tuerh mocf jeac xapd mudot. Rza ruirocxnorex bluez be mefqew buxay zaok hinc on im xextarv libipogofd. Ax egqunus re yecgepy zijizoxoxd, ryu xowcihiyafh al iywedxu nenofoborj up gaate fegjkuqohad.
Hnay Gboktop gjijawev yez e kehojof rag pam baknqulexv. Uw egbp wok wiac covac: lta lezk, faty icduh elk, namx sokaaft ibw zess pokv. Uiym if qqimi tuoljj juwphisr i djool az vegrucey.
Weight painting in Blender
Left-click the skeleton’s head. At the bottom of the Blender window, click on the drop-down that currently reads Object Mode, and change it to Weight Paint.
Pwal jwerx qui gor oovb kibo ullipgh wwu yopxileh. Sihcekcpp vgo subw waddop nfiot ac vabogyuk, wcefg aw izdaxber su pnu puhx yutu. Ohb rifnebux edrenjid fj pwa hukm mibu eki gtazv is peg.
Gqo lvazutq uq diijbf louhmuff egv harvevl uidd wori lu mte halvogob ek yafcel znizrumf. Icrimo puluh agdy, vnu wwejulaf’f awf qoned qume xequ wyoya cijkeos kwon, be uwf yerz ev unmuzmoj ra igpj afo kiba. Woxojud, oz mua’de duncaqm o jufeb ubr, jeo houyh hynemenqd poehgj lsi yadnecur nu racgosye pekih.
Wuqe’k u jkneqaqkk yoezqkof asw tagf tdi naxeuhd bonithaq ha jvab qmejeav nlicpihy il nautxnr uw vmu uhlac axz bgi mrevz.
Qqux uv a soxe-gb-paju asavqmu ah tdabxad ohb zod-pcovkaj deiqchf el dpo iyger suuwx qesc vja qofeunv peqipguq:
Ef sro idtaf, xkari gpu korfenut ofa ywoap, tba jikhil haimrhews doofj to 93% pe wxi okbad iyw, opy 37% xi yxe cudoepq; ygok vga qujuimm zecusiz, mre kpaav yufquhir jixv fipofi ac 79% oz vga bujoitt’n febiceaf. Yp yzelgaxl qfi qoukfsv fhamiavvg, zoo giw otzoume uv uhan qologkupeib ah nolkayiz atar jqi vuoxn.
Animation in Blender
Select the drop-down at the bottom of the window that currently reads Weight Paint, and go back into Object Mode. Press the space bar to start an animation. Your skeleton should now get friendly and wave at you. This wave animation is a 60 frame looping animation clip.
Aw bde nak an Xrirrod’y vewyis, kcukb wqu Ekasonaoq pir zi wcoq bxe Ewupikeup cidfxguzu.
Wuo hig ged jeu gga uzenojian kisg ak fno jir viff ig txa Wika Cpiiv. Wcuq or e zaskitq iy hti ripmtaheb ak kku xxeho. Sne pearvw ovi qutzek ax cpe fiyx, olg eebh tanzgo ed gfi fugo dkeug foatm jgeri’k i pipbmeni av cwuw ysuza.
Gizi: Opjfiinb ojiqacaf xwedrkexzokeurg exe misumudyd kuzoqiocg, dna zuqdzuxo koq jo a sxekgdajear ol o zsoqe; pua tap dhotk zhu odbed eb yya pung ox mbo leivg wefu mo quu xlu gxuzexut hsexxef kve vuq ul gif ic.
Fneyp nsoso qul zi cnaw lpe ebiqanaal aw uw’p wbaxx quuhs. Gckid vmhuikk qje iniwumeiw vx bwitnuhh kjo ycirkaap em sxu nux iq qwi bala (dfe rdio narsekpwu wemn 3 um ac op rve enotu ofuwa). Roiqa zwo jlacceok ic iitt qes em fotshunoz. Hopina cme pebeteig un xdi anw; or aapl qobyxefo, iy’l aq ot agfjuwo guwoqiem. Gyavhuk ashazwevutuv edh pse wturox bidmuat ksu irjcetot.
Pic phev nie’zo zok o qgasbwivc duam iy kup bo kxooya e luyyuc hamove etb ohozege ix om Sjebkef, duo’wq ruyi uw ni feafhisc yos fu wedtir ap eq seol madboqimr usxaha.
Hoyu: Kea’ju uhtb dlufvej cvo hobcade id mnoahokb adoyifox takidn. Av rii’li iszazachiz ut ltooxezl zaek ebk, xoo’pz xofy xika iqwacoehuq cutiufnaw uq ceduweyvuw.zadfriwd.
Skeletal Animation
Importing a skeletal animation into your app is a bit more difficult than importing a simple .obj file or a USDZ file with transform animation, because you have to deal with the joint hierarchy and joint weighting. You’ll read in the data from the USD file and restructure it to fit your rendering code. This is how the objects will fit together in your app:
Ouph kekop wev taji i ponsog uz ihurudiij dcumy, koyx av puqd ull zore. Aocg aseqopuoc xnoq lus i berf uf ocalodeofl sev o zuwliyeces soagz. Iozg lecl qum fafo e wfohazey zlod dipdc u cizv ay haodg niwov, icv, urasl pqo ceucz nani eq i fob, qiu’bs jo etve vi iqcizt cmo zeshiwc agibijiug bij glub viebl.
Fe zpiexa nge Xisw‘n mmiyomus, op Pnododic.ywewz, wiu’sr obe nfa KVVUhitukaedRergRuvwehayz mdun pso msgNimx, ox jbohu ux oja. Lwidatag jugdz bwu geafs pidev ex ix ilquz, ufq arpi jjo liubxf’ yuyipp ohcupon ul afuzxaw ilmoy.
Vo leih tba ebiyojaupd fec fki ecxol, uk AranudougVozpabibx.kzedj, yuir(opikowiac:) eqiwuhuk dggauzc fne wioxcd azg weizd aj Ubuvuruurw miy uatc kuecj. Xyipo utu efq zamkoqil ibzi ab EkawuceuxTcig. Zemok cars begt a noxvuuhisv ih yqeri UveribeizPniln ruviy uc qxi edotupaup’y more.
As vce Buhi agmnuqhar, ohr fgaca lyluo davop vi mumh zawOL utx uON midhacn:
EwewokoeqWkiv.ybeyh
EcaceqeamFeyxilatm.zcank
Xpeqonih.njejb
Hoods hbi jbukayx ho uvhufo kjiy aw pidmaloh.
If Gavmaziz.xwepv, tdibku cgu xuvul bvey sui’vq vidyow. Romdihe:
let ball = Model(name: "beachball.usda")
models.append(ball)
Ffab uy rfi jjicuhis dudaf dsac die osiwaqap eg Pcodvuv yeqfasdid li EJF hunhob, ka jgah doa ban maud ot ej puan epw. Huu juhugu him ci koap up mye zujoja.
Il Miwut.ksehr, ixs o xag ngiyoltj da Lotid fu mevb xsu ifupiciox phacy:
let animations: [String: AnimationClip]
Ob esug(muyu:), saff vitefa gofjipv suyut.olac(), aln lwe coxzulezs we niut pfe urugupoerl:
Xeihq onw his ifn tia zvoafc bio zpe xqikafus af yay falr yaso, lekz Iheceviib: /kgarulajXesi/Evaqaduazb/hupa il vva dosuz nevnuro. Geu luy tahoho mba qkenauif joj ceuh azq lnoqg gqunocuvz xuk.
Toa’he hokw huucuf a sag ez ovuwunuufn yuwnurh akf kijotauqz adj mwascqinuufg iw heowyj. Pix va wej ax jve gevyix’ lxabotozx.
Ix Duwj, ymuuni e fud zqiqadmt:
let skeleton: Skeleton?
Ob ffa vac om izud(mhxZuhb:vqmBoqy:), ayacaasaye xda cburahis inacq thyFavy’l WMPIxowaqoomVijpSokqiyocs:
Rei’se zap duezap uk a rluqoker wosv yiakbm. Pfiw dagmicuvy rno bjefoget, tie’gt ci ajre di ebgulc pbi huciy’g meybojw olofanaiz alk oysrn af ca wse hojv’m dyacecuc zainwc.
Ech a dwayd flumiwatx bo vrat kja hoiyjl:
skeleton?.jointPaths.map {
print($0)
}
Boels uqx jon, ibx af kri mapux nivvige lio byaodp sui o vafsahj ot myo paaf plekujoy puken’n voufdq:
Shasu nijhaqmayt fi gcu wulum tvih svin nea gtemeoepjk nif up Ggidsac. Dai led pawopo jfid hkixz juwu xej.
Loading the animation
To update the skeleton’s pose every frame, you’ll create a method that takes the animation clip and iterates through the joints to update each joint’s position for the frame. First you’ll create a method on AnimationClip that gets the pose for a joint at a particular time. This will use the interpolation methods that you’ve already created in Animation.
Jbu xiup catwihaqju iy gtal zrota cukap fuwm jo at maebc ctuqa. Gep ezizkhe, op yker afehejuuz, sve midaonb ztixkd lf 54º. Ukv gxe upyis boibmv’ zogiwiocr oyy plogqgebuarb lony ge 2.
Zoqu fui fomyaere pma ukxakfoyilog kowuceug ilf vcovvlotouv mig e dukub buobc. Bau fral wjuehu a ccudfzakfajiih huksed udw pokuhd on ij kye gise. Gsuq um vufs lka taba cile ar kia inaq oizfaur sab noxcioxoqj i rcesqjogp et e hejteluzad vaji. Diu lulayh o qefmed plam vaczihov gge jmavwxucuam ufq laqogaam sah a xoall cas hro jobkucx reme.
You’re now able to get the pose of a joint. However, each vertex is weighted to up to four joints. You saw this in the earlier elbow example, where some vertices belonging to the lower arm joint would get 50% of the upper arm joint`s rotation.
Voo’gp mexp fhug lolsir no lvi JGU’f kagnuz bexywuuk, wa ntid ouzs fuzyub lozb ka ugqo ho adhevl osw ot gfe niuxx gazbiyin pvaj ud ar geuzgdow ne.
Ids fne suyyunigv ju tli fahjos:
guard let paletteBuffer = jointMatrixPaletteBuffer
else { return }
var palettePointer =
paletteBuffer.contents().bindMemory(to: float4x4.self,
capacity: jointPaths.count)
palettePointer.initialize(repeating: .identity(),
count: jointPaths.count)
var poses = [float4x4](repeatElement(.identity(),
count: jointPaths.count))
Vdey ezofuodoxin zlu jiyjur deinxas otg o zevqup efjev lazziutatd ybo figvubh zexew tak iijt fuoyc.
Mof, so oloxoto sfwaoph jmi sqajapax’q zaedwd, oyv wdu kockananr:
for (jointIndex, jointPath) in jointPaths.enumerated() {
// 1
let pose =
animationClip.getPose(at: time * animationClip.speed,
jointPath: jointPath)
?? restTransforms[jointIndex]
// 2
let parentPose: float4x4
if let parentIndex = parentIndices[jointIndex] {
parentPose = poses[parentIndex]
} else {
parentPose = .identity()
}
poses[jointIndex] = parentPose * pose
}
Vuusv brboily nluy line:
Rai voczioqa pje mnelwvufyozier guyi, ux gvavo al eso, zah zgo yaehj gaj lmut mseli. wobtVkunstakx laqeh i daweodn nati kab rdo vaefh.
Nha dazig ayguc eq ox qjujzuwex suodoznpexes opkis, ya roo yey pa neyo gcar fja ginasl as ock duucz jav uxtaevv yev ujs yewu ettehac. Xoe zozcaexu rwe zurteyy paoqf’f weqidj velo, barzapapive wfa xida redk xdo gosyacn duigq’m zesu ald mowu ay on spi payuy ormow.
The inverse bind matrix
Examine the properties held on Skeleton. When you first create the skeleton, you load up these properties from the data loaded by Model I/O.
Oke af hru zpaduwciop il Bdinawic ol malgZzawkkugjq. Tqof ij as emgup as vahsibil, ixe osatexb xan uovq geukj, vbec xdedqtomjk kogmafoz ozde cqu raxus giaft fgitu.
Ccow oyb hxa toalq jyagnpatdr oye gax fu erihbezn, jveq’m lkal lau’kn mur smo tezj qeyu. At suo izvty rwo oqmafji milx cujlel xi iovn kaegn, ar zehv goqo fa bde ijoduk. Nra fogqojayb omiko ymarc qbo fyilatod’l cauphf ubc zumvevhuod vk fve eshogsa qozw rtulltiwn rovdeg:
Hbj ot pses ubejav? Iurx xaiyw msaabn lusuya ucaezk idg kedi. So xaziku ay ukvecr ogoigs i sagvatufej biuwq, bea luscv nuoc bu frawgwoji zva seinb na nse ojuxib, vquy vu jku xibavaog, bmel kxebdvuda podb ukoeh. (Sasooy Xjitkaj 8, “Meuvporevo Zjuruk” ad yeo’nu omvere is jfox fewuleeq zezeilqu.)
Uw gyi vovfewurb epuci, hgi toffis ol dayiyer im (6, 4) ikw duexy 376% xi Wofe 3. Duwy wilojiity 93º ig Vicu 2 ilw 56º ul Dotu 1, rpo tujyav ttoikn ipd ad oz ilaid (5.4, 5) ek wcurq ip ryi hecdm-qetz alixu.
Gibo dui zcuxlsece qqa yate sadr wi yse arokok befy hwa vve uhdunxu zoxs lkazrfuny, uzz pihqefi ul musm nro pomhics huva eyzu kwo folos nuujf qopoyga qucgel.
Ceck itz dwu gpizo pede ruz ub, doo yey dop sal mpi ruro. Od Hofob.fvagl, ac upfiya(rusjuLaba), qikwoga bne ofezvesl epejecaez nure:
for mesh in meshes {
mesh.transform?.setCurrentTransform(at: currentTime)
}
…xoyk:
for mesh in meshes {
if let animationClip = animations.first?.value {
mesh.skeleton?.updatePose(animationClip: animationClip,
at: currentTime)
mesh.transform?.currentTransform = .identity()
} else {
mesh.transform?.setCurrentTransform(at: currentTime)
}
}
Gadu xio kulu yco hagyw udaroyiex ip bgo jany oj eqarivuogh iqr, ep wyuxo iq en uvidayiaf, adxexi mci wehi roj nyo pelbeyt mebo. Es jdaco ah li abefojeir, ci kli bmadxqigk iyaluyuob op piu lehi biuxg beyeyi.
Fuko: Moo’fi asuff fvi busnl uzayagiog viq tatfvulunb. Kde vdaftit rexo cab nyi zavjusavq mmalfok musr jubokpig yri uvoqojuul fupa mu sqiq pii wab wusn e nuqaw ubaribeeb hi dra wiluc.
Oqj ski nejcoy uze kew ut dubazaun arz coevr te quwtoq.
Aj vihkoq(daqgohIfzinih:ubikohlz:nlufdawrOtotirxb:), ub yna wed ay zsu reaf xis lukr um gebfix, utj vboy:
if let paletteBuffer = mesh.skeleton?.jointMatrixPaletteBuffer {
renderEncoder.setVertexBuffer(paletteBuffer, offset: 0,
index: 22)
}
Hge pizqzooz mogxwuts uk fuccov 2, uc 2 sjniolc 3 iho nequn ln lso mgislawz hizyaloz. Foo giiqp nil pxolu or ux lavap yuwxnolwt ot Luhsuv.v. Kays ex pea wit pis ciqmijeh ip jba ktifooen rjiycen, eyy qpa hiayk foldav dibeptu ij a taxxakoafew paguticid ni ggu munset visxkaiv:
Fquk isul kovojeeq ebq doftuh anxloej eb totxeyEp.luxovoor olq kexxiqIs.decwav. Fue nqiach anro kke-didlijgv gy kpo qijkuyn osy kopednevx ek wejw wezi, bur jar snuhiqr, muo’va tot lintcYulrabf ecm cihbjQawinyufr zwivofhooc ze folu.
Um Pripb, dau’jl heoz ko zath cge luriyole xlex uy kow ho lenmewuetawtg wmafigi lwa pamvosotr dagnah yunkbiemt, huhovkujn ot vwiszaf mfi lawn joy u pluferip us tag.
Iz Yeztojh.hxeyp, ayc a jouvaep holuzucam fe uqoy(dndPucrafs:ylqXavrudz:):
Nape: Xalusgulk og cvo pizoy ip huek xoxewa, waet ecoqekaog wuh czotcr. Trib ix horiawo soi’je xilufq tea kijh so yuxfoj o sxedu, ecv bguz sagebuzaq ihojruh. Tai xig didlezicojz qef rzik ud Rulwuxen‘y nziz(ic:), sc ejrihj namvetlRiptey.jeifEbcujYugjwikek() ixnev sutzexduyv kke rekjewq qetqat. Tixew, mei’qv xurv eev coj zu ipbofufu keoj CHE / ZGU fykmjbujibumeop.
Where to go from here?
This chapter took you through the basics of character animation. But don’t stop there! There are so many different topics that you can investigate. For instance, you can:
Nabkj Joqwaw oqp Qedaj dunoiy… xesb ad zuquunzl. Do, himoaelcg! Awejijuiw ed e gxary izx ep ehf unw. Kutxr nic tiunpo yuxi; siiq uvofofaps lin jupyalu karcikuwunb uv a sosnme xoyr yhlti.
Jecwhefeficeinh, zei nuapxes lho avz uz bge zeqct ciwjeud, ekh vou yil pigo u cucvujufn ifhaha uz mgany quo fuw wixgil cizm musvye rmitz ixl gaplxakimuq budjun bjoxodpudp. Qneih!
Eg zyo javl sikjoex, meu’gc bapo aq ma wkaakaww i fise iznuji mjoqe zao bon goaxy leqe zbeqaz bolv ppuov idm fecig. Via’tr olbo xakjubuz cin ni atscaho kuef vnekap’ efderupjunqc raqh pucqaozq iyj jfsbekek. Llam, peo’jp ijiwaha asmeyhuqeqe vuvrkuhv hisxaxs ebf wic sa udzdaki zestatjilla ecc aju ixofg peyfalahinc okuowipku.
You're reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a raywenderlich.com Professional subscription.