|
16 | 16 | <meta property="og:image" content="https://tmhsdigital.github.io/Blender-Developer-Tools/gallery/assets/shape-key-blend-hero.webp" /> |
17 | 17 | <meta property="og:image:width" content="1280" /> |
18 | 18 | <meta property="og:image:height" content="720" /> |
19 | | - <meta property="og:image:alt" content="Three purple boxes flaring wider at the top by increasing amounts, from a plain cube to a strong flare." /> |
| 19 | + <meta property="og:image:alt" content="Three purple boxes flaring wider at the top by increasing amounts; the rightmost sits inside a pale wire cage of its full shape key, halfway to it." /> |
20 | 20 | <meta property="og:site_name" content="Blender Developer Tools" /> |
21 | 21 | <meta name="twitter:card" content="summary_large_image" /> |
22 | 22 | <meta name="twitter:title" content="shape-key-blend — Examples — Blender Developer Tools" /> |
23 | 23 | <meta name="twitter:description" content="A relative Tall shape key that lifts and flares the top face — authored via shape_key_add / key_blocks / .value — read back from the depsgraph-evaluated mesh." /> |
24 | 24 | <meta name="twitter:image" content="https://tmhsdigital.github.io/Blender-Developer-Tools/gallery/assets/shape-key-blend-hero.webp" /> |
25 | | - <meta name="twitter:image:alt" content="Three purple boxes flaring wider at the top by increasing amounts, from a plain cube to a strong flare." /> |
| 25 | + <meta name="twitter:image:alt" content="Three purple boxes flaring wider at the top by increasing amounts; the rightmost sits inside a pale wire cage of its full shape key, halfway to it." /> |
26 | 26 | <style> |
27 | 27 | /* fonts are deployed by the landing build (docs/fonts/) */ |
28 | 28 | @font-face { font-family: 'Barlow Condensed'; font-weight: 600; font-display: swap; |
@@ -268,8 +268,8 @@ <h1>shape-key-blend</h1> |
268 | 268 | <p>A relative Tall shape key that lifts and flares the top face — authored via shape_key_add / key_blocks / .value — read back from the depsgraph-evaluated mesh.</p> |
269 | 269 | </header> |
270 | 270 | <main id="main"> |
271 | | - <button class="detail-hero" id="heroZoom" type="button" aria-label="View full size: Three purple boxes flaring wider at the top by increasing amounts, from a plain cube to a strong flare."> |
272 | | - <img src="../assets/shape-key-blend-hero.webp" alt="Three purple boxes flaring wider at the top by increasing amounts, from a plain cube to a strong flare." width="1280" height="720" /> |
| 271 | + <button class="detail-hero" id="heroZoom" type="button" aria-label="View full size: Three purple boxes flaring wider at the top by increasing amounts; the rightmost sits inside a pale wire cage of its full shape key, halfway to it."> |
| 272 | + <img src="../assets/shape-key-blend-hero.webp" alt="Three purple boxes flaring wider at the top by increasing amounts; the rightmost sits inside a pale wire cage of its full shape key, halfway to it." width="1280" height="720" /> |
273 | 273 | </button> |
274 | 274 | <p class="zoom-hint">Rendered headless by the example itself — click to zoom.</p> |
275 | 275 | <div class="callout"><span class="tag">witnesses</span> Shape keys do not rewrite mesh.vertices: every evaluated vert matches basis + value × (key − basis), including the flared top half-extent.</div> |
@@ -460,6 +460,30 @@ <h2>Source</h2> |
460 | 460 | ob.rotation_euler = (<span class="n">0.0</span>, <span class="n">0.0</span>, math.radians(<span class="n">28</span>)) |
461 | 461 | blocks.append(ob) |
462 | 462 |
|
| 463 | + <span class="c"># Endpoint cages on the checked block: its Basis and its full Tall key as</span> |
| 464 | + <span class="c"># pale wireframes, so the solid visibly sits halfway between the two —</span> |
| 465 | + <span class="c"># the closed-form blend the check asserts, not just "a taller box".</span> |
| 466 | + <span class="c"># Render-only copies with their own key value; `obj` is untouched.</span> |
| 467 | + cage_mat = bpy.data.materials.new(<span class="s">"Cage"</span>) |
| 468 | + cage_mat.use_nodes = <span class="k">True</span> |
| 469 | + cbsdf = cage_mat.node_tree.nodes[<span class="s">"Principled BSDF"</span>] |
| 470 | + cbsdf.inputs[<span class="s">"Base Color"</span>].default_value = (<span class="n">0.92</span>, <span class="n">0.84</span>, <span class="n">1.0</span>, <span class="n">1.0</span>) |
| 471 | + cbsdf.inputs[<span class="s">"Roughness"</span>].default_value = <span class="n">0.4</span> |
| 472 | + cages = [] |
| 473 | + <span class="k">for</span> end <span class="k">in</span> (<span class="n">0.0</span>, <span class="n">1.0</span>): |
| 474 | + me = obj.data.copy() |
| 475 | + me.materials.clear() |
| 476 | + me.materials.append(cage_mat) |
| 477 | + me.shape_keys.key_blocks[<span class="s">"Tall"</span>].value = end |
| 478 | + cage = bpy.data.objects.new(<span class="s">f"</span><span class="s">Cage_</span>{end}<span class="s">"</span>, me) |
| 479 | + cage.location = obj.location |
| 480 | + cage.rotation_euler = obj.rotation_euler |
| 481 | + wire = cage.modifiers.new(<span class="s">"cage"</span>, <span class="s">"WIREFRAME"</span>) |
| 482 | + wire.thickness = <span class="n">0.02</span> |
| 483 | + wire.use_even_offset = <span class="k">True</span> |
| 484 | + bpy.context.collection.objects.link(cage) |
| 485 | + cages.append(cage) |
| 486 | + |
463 | 487 | floor_me = bpy.data.meshes.new(<span class="s">"Floor"</span>) |
464 | 488 | bm = bmesh.new() |
465 | 489 | <span class="k">try</span>: |
@@ -487,7 +511,9 @@ <h2>Source</h2> |
487 | 511 |
|
488 | 512 | world_top = HALF + EXPECT_TOP_Z |
489 | 513 | aim = bpy.data.objects.new(<span class="s">"Aim"</span>, <span class="k">None</span>) |
490 | | - aim.location = (<span class="n">0.0</span>, <span class="n">0.0</span>, world_top / <span class="n">2</span>) |
| 514 | + <span class="c"># Aimed above the checked block's middle: the full-key cage over it is</span> |
| 515 | + <span class="c"># taller than any solid, and centring on the solids clipped it.</span> |
| 516 | + aim.location = (<span class="n">0.35</span>, <span class="n">0.0</span>, world_top * <span class="n">0.62</span>) |
491 | 517 | scene.collection.objects.link(aim) |
492 | 518 |
|
493 | 519 | <span class="k">def</span> light(name, loc, energy, size, col): |
@@ -520,7 +546,8 @@ <h2>Source</h2> |
520 | 546 | cam_data = bpy.data.cameras.new(<span class="s">"Cam"</span>) |
521 | 547 | cam_data.lens = <span class="n">50.0</span> |
522 | 548 | cam = bpy.data.objects.new(<span class="s">"Cam"</span>, cam_data) |
523 | | - cam.location = (<span class="n">0.0</span>, -<span class="n">8.8</span>, <span class="n">2.05</span>) |
| 549 | + <span class="c"># Pulled back so the full-key cage on the right block stays in frame.</span> |
| 550 | + cam.location = (<span class="n">0.35</span>, -<span class="n">10.4</span>, <span class="n">2.5</span>) |
524 | 551 | scene.collection.objects.link(cam) |
525 | 552 | scene.camera = cam |
526 | 553 | track = cam.constraints.new(<span class="s">'TRACK_TO'</span>) |
@@ -581,7 +608,7 @@ <h2>Source</h2> |
581 | 608 | </main> |
582 | 609 | <dialog class="lightbox" id="lightbox" aria-label="Full-size render"> |
583 | 610 | <button class="lightbox-close" type="button" autofocus>Close</button> |
584 | | - <img src="../assets/shape-key-blend-hero.webp" alt="Three purple boxes flaring wider at the top by increasing amounts, from a plain cube to a strong flare." /> |
| 611 | + <img src="../assets/shape-key-blend-hero.webp" alt="Three purple boxes flaring wider at the top by increasing amounts; the rightmost sits inside a pale wire cage of its full shape key, halfway to it." /> |
585 | 612 | </dialog> |
586 | 613 | <footer> |
587 | 614 | <div class="statusbar"> |
|
0 commit comments