fogón

← Basics3D assetfogon/racimo-de-uvas

Racimo de uvas

by fogonCC09975 blocks
Racimo de uvasDrag to turn it
// «Racimo de uvas» · gpt-6-sol (medium)
// Grape bunch («Racimo de uvas»).

// Vines and leaves sit behind the grapes.
const wood = ["#59391f", "#785028", "#946734", "#ac7b3d"];
box(23, 25, 33, 25, 27, 41, wood[1]);
box(24, 24, 37, 25, 25, 43, wood[2]);
box(25, 25, 41, 27, 27, 44, wood[1]);
box(26, 24, 43, 27, 25, 45, wood[2]);
line(24, 25, 38, 18, 24, 36, 1, wood[0]);
line(25, 26, 39, 33, 27, 41, 1, wood[1]);

// A squared, curling tendril on the left.
const olive = "#607335";
line(18, 24, 37, 14, 24, 40, 1, olive);
line(14, 24, 40, 9, 24, 39, 0, "#839342");
line(9, 24, 39, 8, 24, 36, 0, olive);
line(8, 24, 36, 11, 24, 34, 0, olive);
line(11, 24, 34, 14, 24, 36, 0, "#91a04c");
line(14, 24, 36, 13, 24, 38, 0, olive);

// Angular green leaves above and to the right of the bunch.
const leafRows = [
  [45, 33, 35], [44, 31, 37], [43, 29, 38],
  [42, 29, 40], [41, 31, 42], [40, 30, 43],
  [39, 28, 41], [38, 28, 39], [37, 29, 37],
  [36, 30, 34]
];
for (const [z, x0, x1] of leafRows) {
  box(x0, 26, z, x1, 28, z, "#426b32");
  box(x0 + 1, 25, z, x1 - 1, 25, z,
      z > 41 ? "#5e873a" : "#54813a");
}
line(28, 24, 37, 39, 24, 41, 0, "#9baa4b");
line(32, 24, 39, 34, 24, 45, 0, "#8ca747");
box(36, 25, 42, 38, 25, 43, "#759746");
box(39, 25, 40, 41, 25, 40, "#9caf50");
box(30, 24, 37, 32, 24, 39, "#789746");
box(33, 25, 44, 34, 25, 45, "#345c31");

// Each grape has a dark rounded underside and irregular lavender highlights.
function grape(cx, cy, cz, rx = 5, ry = 4.5, rz = 4.5) {
  for (let z = Math.floor(cz - rz); z <= Math.ceil(cz + rz); z++) {
    for (let y = Math.floor(cy - ry); y <= Math.ceil(cy + ry); y++) {
      for (let x = Math.floor(cx - rx); x <= Math.ceil(cx + rx); x++) {
        const dx = (x - cx) / rx, dy = (y - cy) / ry, dz = (z - cz) / rz;
        if (dx * dx + dy * dy + dz * dz > 1) continue;

        const light = dz * 0.40 - dx * 0.13 - dy * 0.15;
        let color = light < -0.30 ? "#54285f"
                  : light < -0.05 ? "#693275"
                  : light < 0.22 ? "#824094"
                  : light < 0.46 ? "#a35cb0"
                  : "#be81c8";

        if (rand() < 0.10) {
          color = light > 0.12 ? "#ae6bb9" : "#73377f";
        }
        // Short broken glints across the upper, viewer-facing facets.
        if (dz > 0.25 && dy < -0.33 && dx > -0.65 && dx < 0.56 &&
            (z + Math.floor(x / 3)) % 4 === 0 && rand() < 0.76) {
          color = rand() < 0.35 ? "#d2a0d7" : "#bd7cc9";
        }
        set(x, y, z, color);
      }
    }
  }
}

// Rear grapes give the bunch depth; the front rows form its tapered silhouette.
const rear = [
  [19, 28, 33], [27, 29, 34], [33, 28, 31],
  [14, 28, 26], [23, 29, 26], [32, 29, 25],
  [17, 29, 19], [26, 30, 19], [33, 28, 18],
  [21, 28, 12], [29, 28, 12], [25, 27, 7]
];
for (const p of rear) grape(...p, 5, 4.5, 4.5);

const front = [
  [18, 20, 33], [27, 20, 33], [35, 21, 31],
  [12, 18, 26], [22, 18, 25], [33, 19, 25],
  [16, 19, 19], [26, 19, 18], [35, 20, 18],
  [21, 20, 12], [30, 21, 12],
  [25, 21, 4] // This bottom grape reaches the floor at z = 0.
];
for (const p of front) grape(...p, 5, 4.5, 4.5);