← NaturalezaObjeto 3Dfogon/cactus-saguaro
Cactus saguaro
Arrastrá para girarlo// «Cactus saguaro» · gpt-6-sol (medium)
// A tall saguaro cactus: a thick green column with vertical ribs and two arms bending up, little white spines dotted along it, standing on a patch of sand.
// A sunlit patch of sand.
const sand = ["#e7b96d", "#f1c980", "#f7d897", "#dbab60"];
for (let x = 7; x <= 41; x++) {
for (let y = 10; y <= 38; y++) {
const dx = (x - 24) / 16.5;
const dy = (y - 24) / 13.5;
const edge = dx * dx + dy * dy + 0.055 * Math.sin(x * 1.8 + y * 0.7);
if (edge < 1) {
set(x, y, 0, sand[rand() < 0.16 ? 3 : rand() < 0.45 ? 0 : 1]);
if (edge < 0.72 && rand() > 0.16) {
set(x, y, 1, sand[rand() < 0.12 ? 0 : 2]);
}
if (edge < 0.52 && rand() < 0.035) set(x, y, 2, "#f9e1ab");
}
}
}
// Green shades are shared by the trunk and its two arms.
const green = ["#218b49", "#2ca855", "#35bd5d", "#49cd6b", "#61d778"];
function cactusColor(angle, z) {
const rib = Math.cos(angle * 8);
if (rib < -0.67) return green[0]; // Recess between ribs
if (rib > 0.72) return z > 29 ? green[4] : green[3];
if (Math.sin(angle) < -0.35) return green[2]; // Sunlit front
return green[1];
}
// Thick main column with a gently domed crown and eight vertical ribs.
for (let z = 2; z <= 43; z++) {
let radius = 5.4;
if (z > 38) radius *= Math.sqrt(Math.max(0, 1 - ((z - 38) / 5.4) ** 2));
for (let x = 17; x <= 31; x++) {
for (let y = 17; y <= 31; y++) {
const dx = x - 24, dy = y - 24;
const angle = Math.atan2(dy, dx);
const ribbedRadius = radius + 0.28 * Math.cos(angle * 8);
if (dx * dx + dy * dy <= ribbedRadius * ribbedRadius) {
set(x, y, z, cactusColor(angle, z));
}
}
}
}
// Round, solid arms: each grows outward, elbows upward, and ends in a dome.
function arm(points, radius) {
for (let i = 0; i < points.length - 1; i++) {
const a = points[i], b = points[i + 1];
const vx = b[0] - a[0], vy = b[1] - a[1], vz = b[2] - a[2];
const length2 = vx * vx + vy * vy + vz * vz;
for (let x = Math.floor(Math.min(a[0], b[0]) - radius); x <= Math.ceil(Math.max(a[0], b[0]) + radius); x++) {
for (let y = Math.floor(Math.min(a[1], b[1]) - radius); y <= Math.ceil(Math.max(a[1], b[1]) + radius); y++) {
for (let z = Math.floor(Math.min(a[2], b[2]) - radius); z <= Math.ceil(Math.max(a[2], b[2]) + radius); z++) {
const t = Math.max(0, Math.min(1,
((x - a[0]) * vx + (y - a[1]) * vy + (z - a[2]) * vz) / length2));
const px = a[0] + t * vx, py = a[1] + t * vy, pz = a[2] + t * vz;
if ((x - px) ** 2 + (y - py) ** 2 + (z - pz) ** 2 <= radius * radius) {
const angle = Math.atan2(y - py, x - px);
set(x, y, z, cactusColor(angle, z));
}
}
}
}
}
}
arm([[21, 24, 25], [17, 24, 25], [14, 24, 27], [13, 24, 30], [13, 24, 35]], 2.9);
arm([[27, 24, 18], [31, 24, 18], [35, 24, 21], [36, 24, 25], [36, 24, 31]], 3.0);
// Tiny ivory spines on exposed cactus surfaces, especially its visible front.
const spineSites = [];
forEach((x, y, z, color) => {
if (!green.includes(color) || z < 4) return;
const outward = [
[0, -1, 0], [-1, 0, 0], [1, 0, 0],
[0, 1, 0], [0, 0, 1]
].filter(([dx, dy, dz]) => get(x + dx, y + dy, z + dz) === undefined);
if (outward.length && rand() < (y <= 24 ? 0.105 : 0.055)) {
spineSites.push([x, y, z, outward[Math.floor(rand() * outward.length)]]);
}
});
for (const [x, y, z, [dx, dy, dz]] of spineSites) {
if (get(x + dx, y + dy, z + dz) === undefined) {
set(x + dx, y + dy, z + dz, rand() < 0.28 ? "#fff9da" : "#f5ebc6");
}
}
// A few rounded pebbles and a cheerful little desert bloom.
ellipsoid(14, 17, 2, 2, 1, 1, "#ada897");
set(14, 16, 2, "#cfcbc0");
ellipsoid(34, 31, 2, 2, 2, 1, "#a6a899");
set(34, 30, 3, "#d1d0bd");
set(29, 15, 2, "#378f45");
set(29, 15, 3, "#43b655");
set(29, 15, 4, "#f39147");
set(28, 15, 4, "#ffc15a");
set(30, 15, 4, "#ffc15a");
set(29, 14, 4, "#ffe077");import { draw } from "./assets/voxel";
import { cactusSaguaro } from "./assets/cactus-saguaro";
// En cada cuadro: parado sobre (x, y), el centro de su base.
draw(ctx, cactusSaguaro, x, y, { size: 64, angle: 0, t: performance.now() / 1000 });
// size: lo que mide en px su lado más largo
// angle: giro sobre el piso, en radianes
// t: segundos; si se mueve, se anima solo con eso# En una terminal, en la carpeta del juego
fogon assets add cactus-saguaro
# Con las herramientas de Fogón (conector)
add_assets({ slug: "<tu-juego>", ids: ["cactus-saguaro"] })
# Queda en client/assets/cactus-saguaro.ts
# https://fogon.page/object/cactus-saguaro