[{"data":1,"prerenderedAt":1279},["ShallowReactive",2],{"navigation":3,"\u002Fcli\u002Ffrom-model-to-component":265,"\u002Fcli\u002Ffrom-model-to-component-surround":1274},[4,29,78,181,199,231,241,245,249,253,257,261],{"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,177],{"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},"TSL composables","\u002Fapi\u002Fadvanced\u002Ftsl-composables","3.api\u002F5.advanced\u002Ftsl-composables",{"title":178,"path":179,"stem":180},"WebGPU","\u002Fapi\u002Fadvanced\u002Fweb-gpu","3.api\u002F5.advanced\u002Fweb-gpu",{"title":182,"path":183,"stem":184,"children":185,"icon":188,"titleTemplate":198},"CLI","\u002Fcli","4.cli\u002F1.index",[186,189,194],{"title":187,"path":183,"stem":184,"icon":188},"TresJS CLI","i-lucide-terminal",{"title":190,"path":191,"stem":192,"icon":193},"From model to component","\u002Fcli\u002Ffrom-model-to-component","4.cli\u002F2.from-model-to-component","i-lucide-route",{"title":195,"path":196,"stem":197,"icon":88},"tres gltf","\u002Fcli\u002Fgltf","4.cli\u002F3.gltf","%s · TresJS CLI",{"title":200,"path":201,"stem":202,"children":203},"Cookbook","\u002Fcookbook","5.cookbook",[204,207,211,215,219,223,227],{"title":205,"path":201,"stem":206},"Cookbook 🍳🧑‍🍳","5.cookbook\u002Findex",{"title":208,"path":209,"stem":210},"OrbitControls","\u002Fcookbook\u002Forbit-controls","5.cookbook\u002F1.orbit-controls",{"title":212,"path":213,"stem":214},"Basic Animations","\u002Fcookbook\u002Fbasic-animations","5.cookbook\u002F2.basic-animations",{"title":216,"path":217,"stem":218},"Model Animation","\u002Fcookbook\u002Fmodel-animation","5.cookbook\u002F3.model-animation",{"title":220,"path":221,"stem":222},"Advanced GSAP Animations","\u002Fcookbook\u002Fadvanced-gsap-animations","5.cookbook\u002F4.advanced-gsap-animations",{"title":224,"path":225,"stem":226},"Tweakpane","\u002Fcookbook\u002Ftweakpane","5.cookbook\u002F5.tweakpane",{"title":228,"path":229,"stem":230},"Dynamic components","\u002Fcookbook\u002Ftransition-dynamic","5.cookbook\u002F6.transition-dynamic",{"title":232,"path":233,"stem":234,"children":235},"Community","\u002Fcommunity","6.community\u002F1.index",[236,237],{"title":232,"path":233,"stem":234},{"title":238,"path":239,"stem":240},"Awesome Resources","\u002Fcommunity\u002Fawesome-resources","6.community\u002Fawesome-resources",{"title":242,"path":243,"stem":244},"German Translation","\u002Fde","de",{"title":246,"path":247,"stem":248},"Spanish Translation","\u002Fes","es",{"title":250,"path":251,"stem":252},"French Translation","\u002Ffr","fr",{"title":254,"path":255,"stem":256},"Italian Translation","\u002Fit","it",{"title":258,"path":259,"stem":260},"Japanese Translation","\u002Fjp","jp",{"title":262,"path":263,"stem":264},"Chinese Translation","\u002Fzh","zh",{"id":266,"title":190,"body":267,"description":1267,"extension":1268,"links":1269,"meta":1270,"navigation":1271,"path":191,"seo":1272,"stem":192,"__hash__":1273},"docs\u002F4.cli\u002F2.from-model-to-component.md",{"type":268,"value":269,"toc":1255},"minimark",[270,283,288,295,299,302,492,500,560,563,599,606,610,624,630,634,637,699,706,735,739,742,947,965,969,972,980,983,987,994,1047,1061,1065,1080,1100,1107,1111,1114,1136,1139,1143,1239,1242,1251],[271,272,273,276,277,282],"p",{},[274,275,195],"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",[278,279,280],"a",{"href":196},[274,281,195],{},".",[284,285,287],"h2",{"id":286},"the-team","The team",[271,289,290,291,294],{},"Two people ship a product configurator. An artist owns ",[274,292,293],{},"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.",[284,296,298],{"id":297},"day-one-the-black-box","Day one: the black box",[271,300,301],{},"The developer loads the model the usual way and drops the scene into the canvas:",[303,304,310],"pre",{"className":305,"code":306,"filename":307,"language":308,"meta":309,"style":309},"language-vue shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","\u003Cscript setup lang=\"ts\">\nimport { useGLTF } from '@tresjs\u002Fcientos'\n\nconst { state, nodes } = useGLTF('\u002Fmodels\u002Frobot.glb')\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Cprimitive v-if=\"state\" :object=\"state.scene\" \u002F>\n\u003C\u002Ftemplate>\n","App.vue","vue","",[274,311,312,347,376,382,422,432,437,447,483],{"__ignoreMap":309},[313,314,317,321,325,329,332,335,338,342,344],"span",{"class":315,"line":316},"line",1,[313,318,320],{"class":319},"sMK4o","\u003C",[313,322,324],{"class":323},"swJcz","script",[313,326,328],{"class":327},"spNyl"," setup",[313,330,331],{"class":327}," lang",[313,333,334],{"class":319},"=",[313,336,337],{"class":319},"\"",[313,339,341],{"class":340},"sfazB","ts",[313,343,337],{"class":319},[313,345,346],{"class":319},">\n",[313,348,350,354,357,361,364,367,370,373],{"class":315,"line":349},2,[313,351,353],{"class":352},"s7zQu","import",[313,355,356],{"class":319}," {",[313,358,360],{"class":359},"sTEyZ"," useGLTF",[313,362,363],{"class":319}," }",[313,365,366],{"class":352}," from",[313,368,369],{"class":319}," '",[313,371,372],{"class":340},"@tresjs\u002Fcientos",[313,374,375],{"class":319},"'\n",[313,377,379],{"class":315,"line":378},3,[313,380,381],{"emptyLinePlaceholder":39},"\n",[313,383,385,388,390,393,396,399,402,405,408,411,414,417,419],{"class":315,"line":384},4,[313,386,387],{"class":327},"const",[313,389,356],{"class":319},[313,391,392],{"class":359}," state",[313,394,395],{"class":319},",",[313,397,398],{"class":359}," nodes ",[313,400,401],{"class":319},"}",[313,403,404],{"class":319}," =",[313,406,360],{"class":407},"s2Zo4",[313,409,410],{"class":359},"(",[313,412,413],{"class":319},"'",[313,415,416],{"class":340},"\u002Fmodels\u002Frobot.glb",[313,418,413],{"class":319},[313,420,421],{"class":359},")\n",[313,423,425,428,430],{"class":315,"line":424},5,[313,426,427],{"class":319},"\u003C\u002F",[313,429,324],{"class":323},[313,431,346],{"class":319},[313,433,435],{"class":315,"line":434},6,[313,436,381],{"emptyLinePlaceholder":39},[313,438,440,442,445],{"class":315,"line":439},7,[313,441,320],{"class":319},[313,443,444],{"class":323},"template",[313,446,346],{"class":319},[313,448,450,453,456,459,461,463,466,468,471,473,475,478,480],{"class":315,"line":449},8,[313,451,452],{"class":319},"  \u003C",[313,454,455],{"class":323},"primitive",[313,457,458],{"class":327}," v-if",[313,460,334],{"class":319},[313,462,337],{"class":319},[313,464,465],{"class":340},"state",[313,467,337],{"class":319},[313,469,470],{"class":327}," :object",[313,472,334],{"class":319},[313,474,337],{"class":319},[313,476,477],{"class":340},"state.scene",[313,479,337],{"class":319},[313,481,482],{"class":319}," \u002F>\n",[313,484,486,488,490],{"class":315,"line":485},9,[313,487,427],{"class":319},[313,489,444],{"class":323},[313,491,346],{"class":319},[271,493,494,495,499],{},"It renders. Then the first ticket arrives: ",[496,497,498],"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:",[303,501,504],{"className":502,"code":503,"language":341,"meta":309,"style":309},"language-ts shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","const head = nodes.value.Head \u002F\u002F Object3D, maybe. Is it a Mesh? Does it exist?\n;(head as Mesh).material = hologram\n",[274,505,506,532],{"__ignoreMap":309},[313,507,508,510,513,515,518,520,523,525,528],{"class":315,"line":316},[313,509,387],{"class":327},[313,511,512],{"class":359}," head ",[313,514,334],{"class":319},[313,516,517],{"class":359}," nodes",[313,519,282],{"class":319},[313,521,522],{"class":359},"value",[313,524,282],{"class":319},[313,526,527],{"class":359},"Head ",[313,529,531],{"class":530},"sHwdD","\u002F\u002F Object3D, maybe. Is it a Mesh? Does it exist?\n",[313,533,534,537,540,543,547,550,552,555,557],{"class":315,"line":349},[313,535,536],{"class":319},";",[313,538,539],{"class":359},"(head ",[313,541,542],{"class":352},"as",[313,544,546],{"class":545},"sBMFI"," Mesh",[313,548,549],{"class":359},")",[313,551,282],{"class":319},[313,553,554],{"class":359},"material ",[313,556,334],{"class":319},[313,558,559],{"class":359}," hologram\n",[271,561,562],{},"Three things are true about this line, and none of them are visible in the editor:",[564,565,566,581,588],"ul",{},[567,568,569,572,573,576,577,580],"li",{},[274,570,571],{},"nodes.Head"," is typed as a generic ",[274,574,575],{},"Object3D",". The cast to ",[274,578,579],{},"Mesh"," is a guess.",[567,582,583,584,587],{},"The name ",[274,585,586],{},"Head"," came from a Blender outliner the developer cannot see.",[567,589,590,591,594,595,598],{},"Nothing checks the string. If it is wrong, ",[274,592,593],{},"head"," is ",[274,596,597],{},"undefined"," at runtime and the page throws.",[271,600,601,602,605],{},"The developer gets it working by logging ",[274,603,604],{},"nodes"," to the console and copying names out of it. The\napp ships.",[284,607,609],{"id":608},"day-two-the-re-export","Day two: the re-export",[271,611,612,613,616,617,620,621,282],{},"The artist splits the head into ",[274,614,615],{},"Helmet"," and ",[274,618,619],{},"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 ",[274,622,623],{},"undefined.material",[271,625,626,627,629],{},"This is the failure ",[274,628,195],{}," 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.",[284,631,633],{"id":632},"running-the-command","Running the command",[271,635,636],{},"The developer generates a component from the model instead:",[303,638,642],{"className":639,"code":640,"language":641,"meta":309,"style":309},"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",[274,643,644,655,660,665,670,675,679,684,689,693],{"__ignoreMap":309},[313,645,646,649,652],{"class":315,"line":316},[313,647,648],{"class":545},"tres",[313,650,651],{"class":340}," gltf",[313,653,654],{"class":340}," public\u002Fmodels\u002Frobot.glb\n",[313,656,657],{"class":315,"line":349},[313,658,659],{"class":530},"# ▲ ■ ● Tres  gltf  robot.glb\n",[313,661,662],{"class":315,"line":378},[313,663,664],{"class":530},"#\n",[313,666,667],{"class":315,"line":384},[313,668,669],{"class":530},"# ✔ Parse       12 named nodes · 3 meshes · 2 materials    34ms\n",[313,671,672],{"class":315,"line":424},[313,673,674],{"class":530},"# ✔ Emit        3 slots                                     1ms\n",[313,676,677],{"class":315,"line":434},[313,678,664],{"class":530},[313,680,681],{"class":315,"line":439},[313,682,683],{"class":530},"# ✔ src\u002Fmodels\u002FRobot.gen.vue\n",[313,685,686],{"class":315,"line":449},[313,687,688],{"class":530},"#   slots  Helmet, Face, Body, Base\n",[313,690,691],{"class":315,"line":485},[313,692,664],{"class":530},[313,694,696],{"class":315,"line":695},10,[313,697,698],{"class":530},"# Done in 41ms\n",[271,700,701,702,705],{},"The output is a ",[274,703,704],{},".vue"," file. That single fact changes what the team can do with the model:",[564,707,708,723,729],{},[567,709,710,714,715,718,719,722],{},[711,712,713],"strong",{},"Read it."," Every node is a ",[274,716,717],{},"\u003CTresMesh>"," or ",[274,720,721],{},"\u003CTresGroup>"," with its transform written out. The\ndeveloper sees the structure of the model without opening Blender.",[567,724,725,728],{},[711,726,727],{},"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.",[567,730,731,734],{},[711,732,733],{},"Type-check it."," The component declares the nodes, materials and clips the model actually has.\nThe rest of the app compiles against that declaration.",[284,736,738],{"id":737},"overriding-without-owning","Overriding without owning",[271,740,741],{},"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:",[303,743,745],{"className":305,"code":744,"filename":307,"language":308,"meta":309,"style":309},"\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",[274,746,747,767,784,804,812,816,824,833,861,919,928,938],{"__ignoreMap":309},[313,748,749,751,753,755,757,759,761,763,765],{"class":315,"line":316},[313,750,320],{"class":319},[313,752,324],{"class":323},[313,754,328],{"class":327},[313,756,331],{"class":327},[313,758,334],{"class":319},[313,760,337],{"class":319},[313,762,341],{"class":340},[313,764,337],{"class":319},[313,766,346],{"class":319},[313,768,769,771,774,777,779,782],{"class":315,"line":349},[313,770,353],{"class":352},[313,772,773],{"class":359}," Robot ",[313,775,776],{"class":352},"from",[313,778,369],{"class":319},[313,780,781],{"class":340},"@\u002Fmodels\u002FRobot.gen.vue",[313,783,375],{"class":319},[313,785,786,788,790,793,795,797,799,802],{"class":315,"line":378},[313,787,353],{"class":352},[313,789,356],{"class":319},[313,791,792],{"class":359}," hologram",[313,794,363],{"class":319},[313,796,366],{"class":352},[313,798,369],{"class":319},[313,800,801],{"class":340},".\u002Fmaterials",[313,803,375],{"class":319},[313,805,806,808,810],{"class":315,"line":384},[313,807,427],{"class":319},[313,809,324],{"class":323},[313,811,346],{"class":319},[313,813,814],{"class":315,"line":424},[313,815,381],{"emptyLinePlaceholder":39},[313,817,818,820,822],{"class":315,"line":434},[313,819,320],{"class":319},[313,821,444],{"class":323},[313,823,346],{"class":319},[313,825,826,828,831],{"class":315,"line":439},[313,827,452],{"class":319},[313,829,830],{"class":323},"Robot",[313,832,346],{"class":319},[313,834,835,838,840,843,845,847,849,852,855,857,859],{"class":315,"line":449},[313,836,837],{"class":319},"    \u003C",[313,839,444],{"class":323},[313,841,842],{"class":319}," #",[313,844,615],{"class":327},[313,846,334],{"class":319},[313,848,337],{"class":319},[313,850,851],{"class":319},"{",[313,853,854],{"class":359}," node ",[313,856,401],{"class":319},[313,858,337],{"class":319},[313,860,346],{"class":319},[313,862,863,866,869,872,875,877,879,882,884,886,888,890,893,895,897,900,902,905,908,910,912,915,917],{"class":315,"line":485},[313,864,865],{"class":319},"      \u003C",[313,867,868],{"class":323},"TresMesh",[313,870,871],{"class":319}," :",[313,873,874],{"class":327},"geometry",[313,876,334],{"class":319},[313,878,337],{"class":319},[313,880,881],{"class":359},"node",[313,883,282],{"class":319},[313,885,874],{"class":359},[313,887,337],{"class":319},[313,889,871],{"class":319},[313,891,892],{"class":327},"material",[313,894,334],{"class":319},[313,896,337],{"class":319},[313,898,899],{"class":359},"hologram",[313,901,337],{"class":319},[313,903,904],{"class":319}," @",[313,906,907],{"class":327},"pointerenter",[313,909,334],{"class":319},[313,911,337],{"class":319},[313,913,914],{"class":359},"glow",[313,916,337],{"class":319},[313,918,482],{"class":319},[313,920,921,924,926],{"class":315,"line":695},[313,922,923],{"class":319},"    \u003C\u002F",[313,925,444],{"class":323},[313,927,346],{"class":319},[313,929,931,934,936],{"class":315,"line":930},11,[313,932,933],{"class":319},"  \u003C\u002F",[313,935,830],{"class":323},[313,937,346],{"class":319},[313,939,941,943,945],{"class":315,"line":940},12,[313,942,427],{"class":319},[313,944,444],{"class":323},[313,946,346],{"class":319},[271,948,949,951,952,954,955,957,958,961,962,964],{},[274,950,881],{}," here is a ",[274,953,579],{},", not an ",[274,956,575],{},", 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",[274,959,960],{},"Robot.gen.vue"," without touching ",[274,963,307],{},", so the override survives every export the artist makes.",[284,966,968],{"id":967},"the-rename-becomes-a-type-error","The rename becomes a type error",[271,970,971],{},"On day two the artist renamed a node and the app broke in production. With the component, the same\nrename fails in the editor:",[303,973,978],{"className":974,"code":976,"language":977},[975],"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",[274,979,976],{"__ignoreMap":309},[271,981,982],{},"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.",[284,984,986],{"id":985},"animations-with-names","Animations with names",[271,988,989,990,993],{},"The artist adds an idle loop. The generated component picks up the clip and hands the bound\nactions to ",[274,991,992],{},"@ready",", keyed by a union of the clip names:",[303,995,997],{"className":305,"code":996,"filename":307,"language":308,"meta":309,"style":309},"\u003CRobot @ready=\"({ actions }) => actions.Idle?.play()\" \u002F>\n",[274,998,999],{"__ignoreMap":309},[313,1000,1001,1003,1005,1007,1010,1012,1014,1017,1021,1024,1027,1029,1031,1034,1037,1040,1043,1045],{"class":315,"line":316},[313,1002,320],{"class":319},[313,1004,830],{"class":323},[313,1006,904],{"class":319},[313,1008,1009],{"class":327},"ready",[313,1011,334],{"class":319},[313,1013,337],{"class":319},[313,1015,1016],{"class":319},"({",[313,1018,1020],{"class":1019},"sHdIc"," actions",[313,1022,1023],{"class":319}," })",[313,1025,1026],{"class":327}," =>",[313,1028,1020],{"class":359},[313,1030,282],{"class":319},[313,1032,1033],{"class":359},"Idle",[313,1035,1036],{"class":319},"?.",[313,1038,1039],{"class":407},"play",[313,1041,1042],{"class":359},"()",[313,1044,337],{"class":319},[313,1046,482],{"class":359},[271,1048,1049,1052,1053,1056,1057,1060],{},[274,1050,1051],{},"actions.Idle"," autocompletes. ",[274,1054,1055],{},"actions.Idel"," is a compile error. When the artist ships the clips in\nseparate files, as Mixamo and most asset packs do, ",[274,1058,1059],{},"--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.",[284,1062,1064],{"id":1063},"physics-without-a-spreadsheet","Physics without a spreadsheet",[271,1066,1067,1068,1071,1072,1075,1076,1079],{},"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: ",[274,1069,1070],{},"Floor-col",",\n",[274,1073,1074],{},"Crate-rigid",", ",[274,1077,1078],{},"Trigger-sensor",". The command reads the names:",[303,1081,1083],{"className":639,"code":1082,"language":641,"meta":309,"style":309},"tres gltf public\u002Fmodels\u002Fshowroom.glb --physics rapier\n",[274,1084,1085],{"__ignoreMap":309},[313,1086,1087,1089,1091,1094,1097],{"class":315,"line":316},[313,1088,648],{"class":545},[313,1090,651],{"class":340},[313,1092,1093],{"class":340}," public\u002Fmodels\u002Fshowroom.glb",[313,1095,1096],{"class":340}," --physics",[313,1098,1099],{"class":340}," rapier\n",[271,1101,1102,1103,1106],{},"Each suffixed mesh becomes a ",[274,1104,1105],{},"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.",[284,1108,1110],{"id":1109},"shipping-smaller-shipping-many","Shipping smaller, shipping many",[271,1112,1113],{},"Two flags close the loop for production:",[564,1115,1116,1126],{},[567,1117,1118,1121,1122,1125],{},[274,1119,1120],{},"--transform"," runs the model through glTF-Transform and points the component at a\n",[274,1123,1124],{},"robot-transformed.glb"," that is typically 70 to 90 percent smaller. The original stays untouched.",[567,1127,1128,1131,1132,1135],{},[274,1129,1130],{},"--instance"," batches repeated meshes into an ",[274,1133,1134],{},"InstancedMesh",", so the showroom can render twenty\nrobots for the draw calls of one.",[271,1137,1138],{},"Both are decisions the developer makes once, in the command, and the artist never sees them.",[284,1140,1142],{"id":1141},"what-changed-for-the-team","What changed for the team",[1144,1145,1146,1162],"table",{},[1147,1148,1149],"thead",{},[1150,1151,1152,1156,1159],"tr",{},[1153,1154],"th",{"align":1155},"left",[1153,1157,1158],{"align":1155},"Before",[1153,1160,1161],{"align":1155},"After",[1163,1164,1165,1177,1188,1203,1214,1225],"tbody",{},[1150,1166,1167,1171,1174],{},[1168,1169,1170],"td",{"align":1155},"Where a node's name lives",[1168,1172,1173],{"align":1155},"In a string the developer typed",[1168,1175,1176],{"align":1155},"In a slot the compiler checks",[1150,1178,1179,1182,1185],{},[1168,1180,1181],{"align":1155},"What a renamed node does",[1168,1183,1184],{"align":1155},"Throws in production",[1168,1186,1187],{"align":1155},"Fails to compile",[1150,1189,1190,1193,1200],{},[1168,1191,1192],{"align":1155},"Where an override lives",[1168,1194,1195,1196,1199],{"align":1155},"Inside a ",[274,1197,1198],{},"traverse()"," over the scene",[1168,1201,1202],{"align":1155},"In the parent, as a slot",[1150,1204,1205,1208,1211],{},[1168,1206,1207],{"align":1155},"What a re-export costs",[1168,1209,1210],{"align":1155},"Re-test every override by hand",[1168,1212,1213],{"align":1155},"Re-run one command, read the diff",[1150,1215,1216,1219,1222],{},[1168,1217,1218],{"align":1155},"Who authors physics",[1168,1220,1221],{"align":1155},"The developer, with numbers",[1168,1223,1224],{"align":1155},"The artist, with names",[1150,1226,1227,1230,1233],{},[1168,1228,1229],{"align":1155},"What a reviewer sees",[1168,1231,1232],{"align":1155},"A new binary in the PR",[1168,1234,1235,1236,1238],{"align":1155},"A readable ",[274,1237,704],{}," diff",[271,1240,1241],{},"The artist still exports from Blender. The developer still writes Vue. The command sits between\nthem and turns the handoff into a type.",[1243,1244,1245],"read-more",{"to":196},[271,1246,1247,1248,1250],{},"Every flag, the generated file in full, and the edge cases are in the ",[274,1249,195],{}," reference.",[1252,1253,1254],"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":309,"searchDepth":316,"depth":349,"links":1256},[1257,1258,1259,1260,1261,1262,1263,1264,1265,1266],{"id":286,"depth":349,"text":287},{"id":297,"depth":349,"text":298},{"id":608,"depth":349,"text":609},{"id":632,"depth":349,"text":633},{"id":737,"depth":349,"text":738},{"id":967,"depth":349,"text":968},{"id":985,"depth":349,"text":986},{"id":1063,"depth":349,"text":1064},{"id":1109,"depth":349,"text":1110},{"id":1141,"depth":349,"text":1142},"Why a .glb becomes a Vue single-file component, told through one team shipping one robot.","md",null,{},{"icon":193},{"title":190,"description":1267},"1G_DVOV8mcONuk9pKjWiyXN9vghMKxdU0szblIOlHMQ",[1275,1277],{"title":187,"path":183,"stem":184,"description":1276,"icon":188,"children":-1},"The tres command line tool for TresJS projects.",{"title":195,"path":196,"stem":197,"description":1278,"icon":88,"children":-1},"Generate a typed Vue component from a .glb\u002F.gltf model, with a slot for every node.",1793714879060]