[{"data":1,"prerenderedAt":1261},["ShallowReactive",2],{"navigation":3,"\u002Fcli\u002Ffrom-model-to-component":261,"\u002Fcli\u002Ffrom-model-to-component-surround":1256},[4,29,78,177,195,227,237,241,245,249,253,257],{"title":5,"path":6,"stem":7,"children":8,"icon":27,"titleTemplate":28},"Getting Started","\u002Fgetting-started","1.getting-started\u002F1.index",[9,12,17,22],{"title":10,"path":6,"stem":7,"icon":11},"Introduction","i-lucide-house",{"title":13,"path":14,"stem":15,"icon":16},"Installation","\u002Fgetting-started\u002Finstallation","1.getting-started\u002F2.installation","i-lucide-download",{"title":18,"path":19,"stem":20,"icon":21},"Your First Scene","\u002Fgetting-started\u002Fyour-first-scene","1.getting-started\u002F3.your-first-scene","i-lucide-donut",{"title":23,"path":24,"stem":25,"icon":26},"Upgrade Guide","\u002Fgetting-started\u002Fupgrade-guide","1.getting-started\u002F4.upgrade-guide","i-lucide-circle-arrow-up","i-lucide-rocket","%s · Get Started with TresJS",{"title":30,"path":31,"stem":32,"children":33},"Essentials","\u002Fessentials","2.essentials",[34,36,48],{"title":30,"path":31,"stem":35},"2.essentials\u002Findex",{"title":37,"icon":38,"open":39,"path":40,"stem":41,"children":42,"page":47},"Core Architecture","i-lucide-cpu",true,"\u002Fessentials\u002Fcore-architecture","2.essentials\u002F1.core-architecture",[43],{"title":44,"path":45,"stem":46},"Custom Vue Renderer","\u002Fessentials\u002Fcore-architecture\u002Fcustom-vue-renderer","2.essentials\u002F1.core-architecture\u002F2.custom-vue-renderer",false,{"title":49,"icon":50,"open":39,"path":51,"stem":52,"children":53,"page":47},"Key Concepts","i-lucide-medal","\u002Fessentials\u002Fconcepts","2.essentials\u002F2.concepts",[54,58,62,66,70,74],{"title":55,"path":56,"stem":57},"Declarative vs Imperative","\u002Fessentials\u002Fconcepts\u002Fdeclarative-vs-imperative","2.essentials\u002F2.concepts\u002F1.declarative-vs-imperative",{"title":59,"path":60,"stem":61},"Reactivity","\u002Fessentials\u002Fconcepts\u002Freactivity","2.essentials\u002F2.concepts\u002F2.reactivity",{"title":63,"path":64,"stem":65},"Constructor Arguments","\u002Fessentials\u002Fconcepts\u002Fconstructor-arguments","2.essentials\u002F2.concepts\u002F3.constructor-arguments",{"title":67,"path":68,"stem":69},"Declarative Properties","\u002Fessentials\u002Fconcepts\u002Fdeclarative-properties","2.essentials\u002F2.concepts\u002F4.declarative-properties",{"title":71,"path":72,"stem":73},"Extending the Catalogue","\u002Fessentials\u002Fconcepts\u002Fextending-catalogue","2.essentials\u002F2.concepts\u002F5.extending-catalogue",{"title":75,"path":76,"stem":77},"Child Attachments","\u002Fessentials\u002Fconcepts\u002Fchild-attachments","2.essentials\u002F2.concepts\u002F6.child-attachments",{"title":79,"path":80,"stem":81,"children":82},"API","\u002Fapi","3.api",[83,86,108,135,145,159],{"title":84,"path":80,"stem":85},"TresJS API Reference","3.api\u002F1.index",{"title":87,"icon":88,"open":39,"path":89,"stem":90,"children":91,"page":47},"Components","i-lucide-box","\u002Fapi\u002Fcomponents","3.api\u002F1.components",[92,96,100,104],{"title":93,"path":94,"stem":95},"\u003CTresCanvas \u002F>","\u002Fapi\u002Fcomponents\u002Ftres-canvas","3.api\u002F1.components\u002Ftres-canvas",{"title":97,"path":98,"stem":99},"\u003CTresCanvasContext \u002F>","\u002Fapi\u002Fcomponents\u002Ftres-context","3.api\u002F1.components\u002Ftres-context",{"title":101,"path":102,"stem":103},"Tres Components","\u002Fapi\u002Fcomponents\u002Ftres-objects","3.api\u002F1.components\u002Ftres-objects",{"title":105,"path":106,"stem":107},"TresPortal","\u002Fapi\u002Fcomponents\u002Ftres-portal","3.api\u002F1.components\u002Ftres-portal",{"title":109,"titleTemplate":110,"icon":111,"path":112,"stem":113,"children":114,"page":47},"Composables","%s · TresJS Composables","i-lucide-arrow-right-left","\u002Fapi\u002Fcomposables","3.api\u002F2.composables",[115,119,123,127,131],{"title":116,"path":117,"stem":118},"useTres","\u002Fapi\u002Fcomposables\u002Fuse-tres","3.api\u002F2.composables\u002F1.use-tres",{"title":120,"path":121,"stem":122},"useTresContext","\u002Fapi\u002Fcomposables\u002Fuse-tres-context","3.api\u002F2.composables\u002F2.use-tres-context",{"title":124,"path":125,"stem":126},"useLoop","\u002Fapi\u002Fcomposables\u002Fuse-loop","3.api\u002F2.composables\u002F3.use-loop",{"title":128,"path":129,"stem":130},"useGraph","\u002Fapi\u002Fcomposables\u002Fuse-graph","3.api\u002F2.composables\u002F4.use-graph",{"title":132,"path":133,"stem":134},"useLoader","\u002Fapi\u002Fcomposables\u002Fuse-loader","3.api\u002F2.composables\u002F5.use-loader",{"title":136,"icon":137,"path":138,"stem":139,"children":140,"page":47},"Event handling","i-lucide-mouse-pointer-2","\u002Fapi\u002Fevents","3.api\u002F3.events",[141],{"title":142,"path":143,"stem":144},"Pointer Events","\u002Fapi\u002Fevents\u002Fpointer-events","3.api\u002F3.events\u002F1.pointer-events",{"title":146,"icon":147,"path":148,"stem":149,"children":150,"page":47},"Utils","i-lucide-wrench","\u002Fapi\u002Futils","3.api\u002F4.utils",[151,155],{"title":152,"path":153,"stem":154},"Type Guards","\u002Fapi\u002Futils\u002Ftype-guards","3.api\u002F4.utils\u002F1.type-guards",{"title":156,"path":157,"stem":158},"Directives","\u002Fapi\u002Futils\u002Fdirectives","3.api\u002F4.utils\u002F2.directives",{"title":160,"icon":161,"path":162,"stem":163,"children":164,"page":47},"Advanced","i-lucide-brain-circuit","\u002Fapi\u002Fadvanced","3.api\u002F5.advanced",[165,169,173],{"title":166,"path":167,"stem":168},"Scaling Performance 🚀","\u002Fapi\u002Fadvanced\u002Fperformance","3.api\u002F5.advanced\u002Fperformance",{"title":170,"path":171,"stem":172},"Primitives","\u002Fapi\u002Fadvanced\u002Fprimitives","3.api\u002F5.advanced\u002Fprimitives",{"title":174,"path":175,"stem":176},"WebGPU","\u002Fapi\u002Fadvanced\u002Fweb-gpu","3.api\u002F5.advanced\u002Fweb-gpu",{"title":178,"path":179,"stem":180,"children":181,"icon":184,"titleTemplate":194},"CLI","\u002Fcli","4.cli\u002F1.index",[182,185,190],{"title":183,"path":179,"stem":180,"icon":184},"TresJS CLI","i-lucide-terminal",{"title":186,"path":187,"stem":188,"icon":189},"From model to component","\u002Fcli\u002Ffrom-model-to-component","4.cli\u002F2.from-model-to-component","i-lucide-route",{"title":191,"path":192,"stem":193,"icon":88},"tres gltf","\u002Fcli\u002Fgltf","4.cli\u002F3.gltf","%s · TresJS CLI",{"title":196,"path":197,"stem":198,"children":199},"Cookbook","\u002Fcookbook","5.cookbook",[200,203,207,211,215,219,223],{"title":201,"path":197,"stem":202},"Cookbook 🍳🧑‍🍳","5.cookbook\u002Findex",{"title":204,"path":205,"stem":206},"OrbitControls","\u002Fcookbook\u002Forbit-controls","5.cookbook\u002F1.orbit-controls",{"title":208,"path":209,"stem":210},"Basic Animations","\u002Fcookbook\u002Fbasic-animations","5.cookbook\u002F2.basic-animations",{"title":212,"path":213,"stem":214},"Model Animation","\u002Fcookbook\u002Fmodel-animation","5.cookbook\u002F3.model-animation",{"title":216,"path":217,"stem":218},"Advanced GSAP Animations","\u002Fcookbook\u002Fadvanced-gsap-animations","5.cookbook\u002F4.advanced-gsap-animations",{"title":220,"path":221,"stem":222},"Tweakpane","\u002Fcookbook\u002Ftweakpane","5.cookbook\u002F5.tweakpane",{"title":224,"path":225,"stem":226},"Dynamic components","\u002Fcookbook\u002Ftransition-dynamic","5.cookbook\u002F6.transition-dynamic",{"title":228,"path":229,"stem":230,"children":231},"Community","\u002Fcommunity","6.community\u002F1.index",[232,233],{"title":228,"path":229,"stem":230},{"title":234,"path":235,"stem":236},"Awesome Resources","\u002Fcommunity\u002Fawesome-resources","6.community\u002Fawesome-resources",{"title":238,"path":239,"stem":240},"German Translation","\u002Fde","de",{"title":242,"path":243,"stem":244},"Spanish Translation","\u002Fes","es",{"title":246,"path":247,"stem":248},"French Translation","\u002Ffr","fr",{"title":250,"path":251,"stem":252},"Italian Translation","\u002Fit","it",{"title":254,"path":255,"stem":256},"Japanese Translation","\u002Fjp","jp",{"title":258,"path":259,"stem":260},"Chinese Translation","\u002Fzh","zh",{"id":262,"title":186,"body":263,"description":1249,"extension":1250,"links":1251,"meta":1252,"navigation":1253,"path":187,"seo":1254,"stem":188,"__hash__":1255},"docs\u002F4.cli\u002F2.from-model-to-component.md",{"type":264,"value":265,"toc":1237},"minimark",[266,279,284,291,295,298,479,487,542,545,581,588,592,606,612,616,619,681,688,717,721,724,929,947,951,954,962,965,969,976,1029,1043,1047,1062,1082,1089,1093,1096,1118,1121,1125,1221,1224,1233],[267,268,269,272,273,278],"p",{},[270,271,191],"code",{}," exists because a loaded model is a black box, and a Vue component is not. This page\nfollows one small team through the difference. If you want the reference instead, go to\n",[274,275,276],"a",{"href":192},[270,277,191],{},".",[280,281,283],"h2",{"id":282},"the-team","The team",[267,285,286,287,290],{},"Two people ship a product configurator. An artist owns ",[270,288,289],{},"robot.glb"," in Blender and re-exports it\nseveral times a week. A Vue developer owns the app and has never opened Blender. Both want the\nsame thing: change the model without breaking the app, and change the app without touching the\nmodel.",[280,292,294],{"id":293},"day-one-the-black-box","Day one: the black box",[267,296,297],{},"The developer loads the model the usual way and drops the scene into the canvas:",[299,300,306],"pre",{"className":301,"code":302,"filename":303,"language":304,"meta":305,"style":305},"language-vue shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","\u003Cscript setup lang=\"ts\">\nimport { useGLTF } from '@tresjs\u002Fcientos'\n\nconst { scene, nodes } = await useGLTF('\u002Fmodels\u002Frobot.glb')\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Cprimitive :object=\"scene\" \u002F>\n\u003C\u002Ftemplate>\n","App.vue","vue","",[270,307,308,343,372,378,421,431,436,446,470],{"__ignoreMap":305},[309,310,313,317,321,325,328,331,334,338,340],"span",{"class":311,"line":312},"line",1,[309,314,316],{"class":315},"sMK4o","\u003C",[309,318,320],{"class":319},"swJcz","script",[309,322,324],{"class":323},"spNyl"," setup",[309,326,327],{"class":323}," lang",[309,329,330],{"class":315},"=",[309,332,333],{"class":315},"\"",[309,335,337],{"class":336},"sfazB","ts",[309,339,333],{"class":315},[309,341,342],{"class":315},">\n",[309,344,346,350,353,357,360,363,366,369],{"class":311,"line":345},2,[309,347,349],{"class":348},"s7zQu","import",[309,351,352],{"class":315}," {",[309,354,356],{"class":355},"sTEyZ"," useGLTF",[309,358,359],{"class":315}," }",[309,361,362],{"class":348}," from",[309,364,365],{"class":315}," '",[309,367,368],{"class":336},"@tresjs\u002Fcientos",[309,370,371],{"class":315},"'\n",[309,373,375],{"class":311,"line":374},3,[309,376,377],{"emptyLinePlaceholder":39},"\n",[309,379,381,384,386,389,392,395,398,401,404,407,410,413,416,418],{"class":311,"line":380},4,[309,382,383],{"class":323},"const",[309,385,352],{"class":315},[309,387,388],{"class":355}," scene",[309,390,391],{"class":315},",",[309,393,394],{"class":355}," nodes ",[309,396,397],{"class":315},"}",[309,399,400],{"class":315}," =",[309,402,403],{"class":348}," await",[309,405,356],{"class":406},"s2Zo4",[309,408,409],{"class":355},"(",[309,411,412],{"class":315},"'",[309,414,415],{"class":336},"\u002Fmodels\u002Frobot.glb",[309,417,412],{"class":315},[309,419,420],{"class":355},")\n",[309,422,424,427,429],{"class":311,"line":423},5,[309,425,426],{"class":315},"\u003C\u002F",[309,428,320],{"class":319},[309,430,342],{"class":315},[309,432,434],{"class":311,"line":433},6,[309,435,377],{"emptyLinePlaceholder":39},[309,437,439,441,444],{"class":311,"line":438},7,[309,440,316],{"class":315},[309,442,443],{"class":319},"template",[309,445,342],{"class":315},[309,447,449,452,455,458,460,462,465,467],{"class":311,"line":448},8,[309,450,451],{"class":315},"  \u003C",[309,453,454],{"class":319},"primitive",[309,456,457],{"class":323}," :object",[309,459,330],{"class":315},[309,461,333],{"class":315},[309,463,464],{"class":336},"scene",[309,466,333],{"class":315},[309,468,469],{"class":315}," \u002F>\n",[309,471,473,475,477],{"class":311,"line":472},9,[309,474,426],{"class":315},[309,476,443],{"class":319},[309,478,342],{"class":315},[267,480,481,482,486],{},"It renders. Then the first ticket arrives: ",[483,484,485],"em",{},"the head should glow when hovered",". The developer now\nneeds one mesh out of the scene graph, and the only handle is a string:",[299,488,491],{"className":489,"code":490,"language":337,"meta":305,"style":305},"language-ts shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","const head = nodes.Head \u002F\u002F Object3D, maybe. Is it a Mesh? Does it exist?\n;(head as Mesh).material = hologram\n",[270,492,493,514],{"__ignoreMap":305},[309,494,495,497,500,502,505,507,510],{"class":311,"line":312},[309,496,383],{"class":323},[309,498,499],{"class":355}," head ",[309,501,330],{"class":315},[309,503,504],{"class":355}," nodes",[309,506,278],{"class":315},[309,508,509],{"class":355},"Head ",[309,511,513],{"class":512},"sHwdD","\u002F\u002F Object3D, maybe. Is it a Mesh? Does it exist?\n",[309,515,516,519,522,525,529,532,534,537,539],{"class":311,"line":345},[309,517,518],{"class":315},";",[309,520,521],{"class":355},"(head ",[309,523,524],{"class":348},"as",[309,526,528],{"class":527},"sBMFI"," Mesh",[309,530,531],{"class":355},")",[309,533,278],{"class":315},[309,535,536],{"class":355},"material ",[309,538,330],{"class":315},[309,540,541],{"class":355}," hologram\n",[267,543,544],{},"Three things are true about this line, and none of them are visible in the editor:",[546,547,548,563,570],"ul",{},[549,550,551,554,555,558,559,562],"li",{},[270,552,553],{},"nodes.Head"," is typed as a generic ",[270,556,557],{},"Object3D",". The cast to ",[270,560,561],{},"Mesh"," is a guess.",[549,564,565,566,569],{},"The name ",[270,567,568],{},"Head"," came from a Blender outliner the developer cannot see.",[549,571,572,573,576,577,580],{},"Nothing checks the string. If it is wrong, ",[270,574,575],{},"head"," is ",[270,578,579],{},"undefined"," at runtime and the page throws.",[267,582,583,584,587],{},"The developer gets it working by logging ",[270,585,586],{},"nodes"," to the console and copying names out of it. The\napp ships.",[280,589,591],{"id":590},"day-two-the-re-export","Day two: the re-export",[267,593,594,595,598,599,602,603,278],{},"The artist splits the head into ",[270,596,597],{},"Helmet"," and ",[270,600,601],{},"Face"," and re-exports. No code changed, so no test\nfails and no type error appears. The build is green. In the browser, the head no longer glows, and\nin production it throws on ",[270,604,605],{},"undefined.material",[267,607,608,609,611],{},"This is the failure ",[270,610,191],{}," is built around. The contract between the model and the app lived\nin a string, and a string cannot tell you when it goes stale.",[280,613,615],{"id":614},"running-the-command","Running the command",[267,617,618],{},"The developer generates a component from the model instead:",[299,620,624],{"className":621,"code":622,"language":623,"meta":305,"style":305},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","tres gltf public\u002Fmodels\u002Frobot.glb\n# ▲ ■ ● Tres  gltf  robot.glb\n#\n# ✔ Parse       12 named nodes · 3 meshes · 2 materials    34ms\n# ✔ Emit        3 slots                                     1ms\n#\n# ✔ src\u002Fmodels\u002FRobot.gen.vue\n#   slots  Helmet, Face, Body, Base\n#\n# Done in 41ms\n","bash",[270,625,626,637,642,647,652,657,661,666,671,675],{"__ignoreMap":305},[309,627,628,631,634],{"class":311,"line":312},[309,629,630],{"class":527},"tres",[309,632,633],{"class":336}," gltf",[309,635,636],{"class":336}," public\u002Fmodels\u002Frobot.glb\n",[309,638,639],{"class":311,"line":345},[309,640,641],{"class":512},"# ▲ ■ ● Tres  gltf  robot.glb\n",[309,643,644],{"class":311,"line":374},[309,645,646],{"class":512},"#\n",[309,648,649],{"class":311,"line":380},[309,650,651],{"class":512},"# ✔ Parse       12 named nodes · 3 meshes · 2 materials    34ms\n",[309,653,654],{"class":311,"line":423},[309,655,656],{"class":512},"# ✔ Emit        3 slots                                     1ms\n",[309,658,659],{"class":311,"line":433},[309,660,646],{"class":512},[309,662,663],{"class":311,"line":438},[309,664,665],{"class":512},"# ✔ src\u002Fmodels\u002FRobot.gen.vue\n",[309,667,668],{"class":311,"line":448},[309,669,670],{"class":512},"#   slots  Helmet, Face, Body, Base\n",[309,672,673],{"class":311,"line":472},[309,674,646],{"class":512},[309,676,678],{"class":311,"line":677},10,[309,679,680],{"class":512},"# Done in 41ms\n",[267,682,683,684,687],{},"The output is a ",[270,685,686],{},".vue"," file. That single fact changes what the team can do with the model:",[546,689,690,705,711],{},[549,691,692,696,697,700,701,704],{},[693,694,695],"strong",{},"Read it."," Every node is a ",[270,698,699],{},"\u003CTresMesh>"," or ",[270,702,703],{},"\u003CTresGroup>"," with its transform written out. The\ndeveloper sees the structure of the model without opening Blender.",[549,706,707,710],{},[693,708,709],{},"Diff it."," The next re-export produces a new file, and the pull request shows exactly which\nnodes moved, appeared or disappeared. The artist's change is reviewable like any other change.",[549,712,713,716],{},[693,714,715],{},"Type-check it."," The component declares the nodes, materials and clips the model actually has.\nThe rest of the app compiles against that declaration.",[280,718,720],{"id":719},"overriding-without-owning","Overriding without owning",[267,722,723],{},"The generated file is disposable. The head glow does not go in it. It goes in the parent, as a\nslot override, and the generated markup stays as the fallback:",[299,725,727],{"className":301,"code":726,"filename":303,"language":304,"meta":305,"style":305},"\u003Cscript setup lang=\"ts\">\nimport Robot from '@\u002Fmodels\u002FRobot.gen.vue'\nimport { hologram } from '.\u002Fmaterials'\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003CRobot>\n    \u003Ctemplate #Helmet=\"{ node }\">\n      \u003CTresMesh :geometry=\"node.geometry\" :material=\"hologram\" @pointerenter=\"glow\" \u002F>\n    \u003C\u002Ftemplate>\n  \u003C\u002FRobot>\n\u003C\u002Ftemplate>\n",[270,728,729,749,766,786,794,798,806,815,843,901,910,920],{"__ignoreMap":305},[309,730,731,733,735,737,739,741,743,745,747],{"class":311,"line":312},[309,732,316],{"class":315},[309,734,320],{"class":319},[309,736,324],{"class":323},[309,738,327],{"class":323},[309,740,330],{"class":315},[309,742,333],{"class":315},[309,744,337],{"class":336},[309,746,333],{"class":315},[309,748,342],{"class":315},[309,750,751,753,756,759,761,764],{"class":311,"line":345},[309,752,349],{"class":348},[309,754,755],{"class":355}," Robot ",[309,757,758],{"class":348},"from",[309,760,365],{"class":315},[309,762,763],{"class":336},"@\u002Fmodels\u002FRobot.gen.vue",[309,765,371],{"class":315},[309,767,768,770,772,775,777,779,781,784],{"class":311,"line":374},[309,769,349],{"class":348},[309,771,352],{"class":315},[309,773,774],{"class":355}," hologram",[309,776,359],{"class":315},[309,778,362],{"class":348},[309,780,365],{"class":315},[309,782,783],{"class":336},".\u002Fmaterials",[309,785,371],{"class":315},[309,787,788,790,792],{"class":311,"line":380},[309,789,426],{"class":315},[309,791,320],{"class":319},[309,793,342],{"class":315},[309,795,796],{"class":311,"line":423},[309,797,377],{"emptyLinePlaceholder":39},[309,799,800,802,804],{"class":311,"line":433},[309,801,316],{"class":315},[309,803,443],{"class":319},[309,805,342],{"class":315},[309,807,808,810,813],{"class":311,"line":438},[309,809,451],{"class":315},[309,811,812],{"class":319},"Robot",[309,814,342],{"class":315},[309,816,817,820,822,825,827,829,831,834,837,839,841],{"class":311,"line":448},[309,818,819],{"class":315},"    \u003C",[309,821,443],{"class":319},[309,823,824],{"class":315}," #",[309,826,597],{"class":323},[309,828,330],{"class":315},[309,830,333],{"class":315},[309,832,833],{"class":315},"{",[309,835,836],{"class":355}," node ",[309,838,397],{"class":315},[309,840,333],{"class":315},[309,842,342],{"class":315},[309,844,845,848,851,854,857,859,861,864,866,868,870,872,875,877,879,882,884,887,890,892,894,897,899],{"class":311,"line":472},[309,846,847],{"class":315},"      \u003C",[309,849,850],{"class":319},"TresMesh",[309,852,853],{"class":315}," :",[309,855,856],{"class":323},"geometry",[309,858,330],{"class":315},[309,860,333],{"class":315},[309,862,863],{"class":355},"node",[309,865,278],{"class":315},[309,867,856],{"class":355},[309,869,333],{"class":315},[309,871,853],{"class":315},[309,873,874],{"class":323},"material",[309,876,330],{"class":315},[309,878,333],{"class":315},[309,880,881],{"class":355},"hologram",[309,883,333],{"class":315},[309,885,886],{"class":315}," @",[309,888,889],{"class":323},"pointerenter",[309,891,330],{"class":315},[309,893,333],{"class":315},[309,895,896],{"class":355},"glow",[309,898,333],{"class":315},[309,900,469],{"class":315},[309,902,903,906,908],{"class":311,"line":677},[309,904,905],{"class":315},"    \u003C\u002F",[309,907,443],{"class":319},[309,909,342],{"class":315},[309,911,913,916,918],{"class":311,"line":912},11,[309,914,915],{"class":315},"  \u003C\u002F",[309,917,812],{"class":319},[309,919,342],{"class":315},[309,921,923,925,927],{"class":311,"line":922},12,[309,924,426],{"class":315},[309,926,443],{"class":319},[309,928,342],{"class":315},[267,930,931,933,934,936,937,939,940,943,944,946],{},[270,932,863],{}," here is a ",[270,935,561],{},", not an ",[270,938,557],{},", because the generator saw a mesh at that node and said\nso. The cast is gone. The console logging is gone. And the next re-export overwrites\n",[270,941,942],{},"Robot.gen.vue"," without touching ",[270,945,303],{},", so the override survives every export the artist makes.",[280,948,950],{"id":949},"the-rename-becomes-a-type-error","The rename becomes a type error",[267,952,953],{},"On day two the artist renamed a node and the app broke in production. With the component, the same\nrename fails in the editor:",[299,955,960],{"className":956,"code":958,"language":959},[957],"language-text","App.vue:9:15 - error TS2339: Property 'Head' does not exist on type\n  '{ Helmet: (props: { node: Mesh }) => any; Face: …; Body: …; Base: … }'.\n","text",[270,961,958],{"__ignoreMap":305},[267,963,964],{},"The developer reads the new slot names off the error, updates the override, and the failure never\nreaches a browser. The contract now lives in a type, and types go stale loudly.",[280,966,968],{"id":967},"animations-with-names","Animations with names",[267,970,971,972,975],{},"The artist adds an idle loop. The generated component picks up the clip and hands the bound\nactions to ",[270,973,974],{},"@ready",", keyed by a union of the clip names:",[299,977,979],{"className":301,"code":978,"filename":303,"language":304,"meta":305,"style":305},"\u003CRobot @ready=\"({ actions }) => actions.Idle?.play()\" \u002F>\n",[270,980,981],{"__ignoreMap":305},[309,982,983,985,987,989,992,994,996,999,1003,1006,1009,1011,1013,1016,1019,1022,1025,1027],{"class":311,"line":312},[309,984,316],{"class":315},[309,986,812],{"class":319},[309,988,886],{"class":315},[309,990,991],{"class":323},"ready",[309,993,330],{"class":315},[309,995,333],{"class":315},[309,997,998],{"class":315},"({",[309,1000,1002],{"class":1001},"sHdIc"," actions",[309,1004,1005],{"class":315}," })",[309,1007,1008],{"class":323}," =>",[309,1010,1002],{"class":355},[309,1012,278],{"class":315},[309,1014,1015],{"class":355},"Idle",[309,1017,1018],{"class":315},"?.",[309,1020,1021],{"class":406},"play",[309,1023,1024],{"class":355},"()",[309,1026,333],{"class":315},[309,1028,469],{"class":355},[267,1030,1031,1034,1035,1038,1039,1042],{},[270,1032,1033],{},"actions.Idle"," autocompletes. ",[270,1036,1037],{},"actions.Idel"," is a compile error. When the artist ships the clips in\nseparate files, as Mixamo and most asset packs do, ",[270,1040,1041],{},"--animations"," merges them and the CLI checks\neach clip's tracks against the model's node names, which is the one animation failure that is\notherwise silent at runtime.",[280,1044,1046],{"id":1045},"physics-without-a-spreadsheet","Physics without a spreadsheet",[267,1048,1049,1050,1053,1054,1057,1058,1061],{},"The configurator grows a showroom floor the robot can fall onto. Instead of the developer measuring\nboxes and typing collider sizes, the artist names the meshes in Blender: ",[270,1051,1052],{},"Floor-col",",\n",[270,1055,1056],{},"Crate-rigid",", ",[270,1059,1060],{},"Trigger-sensor",". The command reads the names:",[299,1063,1065],{"className":621,"code":1064,"language":623,"meta":305,"style":305},"tres gltf public\u002Fmodels\u002Fshowroom.glb --physics rapier\n",[270,1066,1067],{"__ignoreMap":305},[309,1068,1069,1071,1073,1076,1079],{"class":311,"line":312},[309,1070,630],{"class":527},[309,1072,633],{"class":336},[309,1074,1075],{"class":336}," public\u002Fmodels\u002Fshowroom.glb",[309,1077,1078],{"class":336}," --physics",[309,1080,1081],{"class":336}," rapier\n",[267,1083,1084,1085,1088],{},"Each suffixed mesh becomes a ",[270,1086,1087],{},"RigidBody"," sized from its own geometry when the model loads. The\nartist reshapes the floor, the collider follows, and no number in the codebase has to be updated.",[280,1090,1092],{"id":1091},"shipping-smaller-shipping-many","Shipping smaller, shipping many",[267,1094,1095],{},"Two flags close the loop for production:",[546,1097,1098,1108],{},[549,1099,1100,1103,1104,1107],{},[270,1101,1102],{},"--transform"," runs the model through glTF-Transform and points the component at a\n",[270,1105,1106],{},"robot-transformed.glb"," that is typically 70 to 90 percent smaller. The original stays untouched.",[549,1109,1110,1113,1114,1117],{},[270,1111,1112],{},"--instance"," batches repeated meshes into an ",[270,1115,1116],{},"InstancedMesh",", so the showroom can render twenty\nrobots for the draw calls of one.",[267,1119,1120],{},"Both are decisions the developer makes once, in the command, and the artist never sees them.",[280,1122,1124],{"id":1123},"what-changed-for-the-team","What changed for the team",[1126,1127,1128,1144],"table",{},[1129,1130,1131],"thead",{},[1132,1133,1134,1138,1141],"tr",{},[1135,1136],"th",{"align":1137},"left",[1135,1139,1140],{"align":1137},"Before",[1135,1142,1143],{"align":1137},"After",[1145,1146,1147,1159,1170,1185,1196,1207],"tbody",{},[1132,1148,1149,1153,1156],{},[1150,1151,1152],"td",{"align":1137},"Where a node's name lives",[1150,1154,1155],{"align":1137},"In a string the developer typed",[1150,1157,1158],{"align":1137},"In a slot the compiler checks",[1132,1160,1161,1164,1167],{},[1150,1162,1163],{"align":1137},"What a renamed node does",[1150,1165,1166],{"align":1137},"Throws in production",[1150,1168,1169],{"align":1137},"Fails to compile",[1132,1171,1172,1175,1182],{},[1150,1173,1174],{"align":1137},"Where an override lives",[1150,1176,1177,1178,1181],{"align":1137},"Inside a ",[270,1179,1180],{},"traverse()"," over the scene",[1150,1183,1184],{"align":1137},"In the parent, as a slot",[1132,1186,1187,1190,1193],{},[1150,1188,1189],{"align":1137},"What a re-export costs",[1150,1191,1192],{"align":1137},"Re-test every override by hand",[1150,1194,1195],{"align":1137},"Re-run one command, read the diff",[1132,1197,1198,1201,1204],{},[1150,1199,1200],{"align":1137},"Who authors physics",[1150,1202,1203],{"align":1137},"The developer, with numbers",[1150,1205,1206],{"align":1137},"The artist, with names",[1132,1208,1209,1212,1215],{},[1150,1210,1211],{"align":1137},"What a reviewer sees",[1150,1213,1214],{"align":1137},"A new binary in the PR",[1150,1216,1217,1218,1220],{"align":1137},"A readable ",[270,1219,686],{}," diff",[267,1222,1223],{},"The artist still exports from Blender. The developer still writes Vue. The command sits between\nthem and turns the handoff into a type.",[1225,1226,1227],"read-more",{"to":192},[267,1228,1229,1230,1232],{},"Every flag, the generated file in full, and the edge cases are in the ",[270,1231,191],{}," reference.",[1234,1235,1236],"style",{},"html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}",{"title":305,"searchDepth":312,"depth":345,"links":1238},[1239,1240,1241,1242,1243,1244,1245,1246,1247,1248],{"id":282,"depth":345,"text":283},{"id":293,"depth":345,"text":294},{"id":590,"depth":345,"text":591},{"id":614,"depth":345,"text":615},{"id":719,"depth":345,"text":720},{"id":949,"depth":345,"text":950},{"id":967,"depth":345,"text":968},{"id":1045,"depth":345,"text":1046},{"id":1091,"depth":345,"text":1092},{"id":1123,"depth":345,"text":1124},"Why a .glb becomes a Vue single-file component, told through one team shipping one robot.","md",null,{},{"icon":189},{"title":186,"description":1249},"ZBbozL-oEcpiR_TwOqcVm6arzw_QJLuQ0PEI-J-DU7g",[1257,1259],{"title":183,"path":179,"stem":180,"description":1258,"icon":184,"children":-1},"The tres command line tool for TresJS projects.",{"title":191,"path":192,"stem":193,"description":1260,"icon":88,"children":-1},"Generate a typed Vue component from a .glb\u002F.gltf model, with a slot for every node.",1790759274152]