← BásicoObjeto 3Dfogon/mesa-tronco
Mesa tronco
Arrastrá para girarlo// «Mesa tronco» · gpt-6-sol (medium)
// Stump table («Mesa tronco»).
const cx = 24, cy = 24;
const bark = ["#643b24", "#754429", "#87512d", "#995d32", "#ad6c3a"];
const grain = ["#f7cc80", "#f9d38a", "#f3c477", "#edb76a", "#ffdc96"];
function hash(x, y, z = 0) {
const n = Math.sin(x * 127.1 + y * 311.7 + z * 74.7) * 43758.5453;
return n - Math.floor(n);
}
// Uneven, solid trunk with long vertical bark grain.
for (let z = 2; z <= 30; z++) {
for (let x = 10; x <= 38; x++) for (let y = 10; y <= 38; y++) {
const dx = x - cx, dy = y - cy;
const a = Math.atan2(dy, dx);
const r = Math.hypot(dx, dy);
const flare = z < 7 ? (7 - z) * 0.36 : 0;
const edge = 9.5 + flare
+ 0.65 * Math.sin(a * 11 + 0.4)
+ 0.42 * Math.sin(a * 19 - 1.1)
+ 0.23 * Math.sin(z * 0.43 + a * 7);
if (r > edge) continue;
const stripe = Math.sin(a * 29 + 0.45 * Math.sin(z * 0.23 + a * 8));
const fleck = hash(x, y, Math.floor(z / 3));
let shade = stripe > 0.52 ? 3 : stripe < -0.55 ? 0 : 2;
if (r > edge - 2.2) {
shade += stripe > 0.75 ? 1 : 0;
if (fleck < 0.10) shade--;
if (fleck > 0.91) shade++;
}
set(x, y, z, bark[Math.max(0, Math.min(4, shade))]);
}
}
// Raised bark strips, staggered breaks and dark fissures.
for (let i = 0; i < 46; i++) {
const a = i * Math.PI * 2 / 46;
const rr = 9.7 + 0.55 * Math.sin(a * 11 + 0.4);
const x = Math.round(cx + Math.cos(a) * rr);
const y = Math.round(cy + Math.sin(a) * rr);
for (let z = 6 + Math.floor(hash(i, 3) * 5); z < 29; z++) {
if (hash(i, Math.floor(z / 5)) < 0.14) continue;
const c = i % 5 === 0 ? "#56341f" :
i % 4 === 0 ? "#ae6e3b" :
i % 3 === 0 ? "#85502c" : "#996035";
set(x, y, z, hash(i, z) < 0.09 ? "#b3753e" : c);
}
}
// Splayed, knobby roots at the foot.
for (let i = 0; i < 19; i++) {
const a = i * Math.PI * 2 / 19 + 0.15 * Math.sin(i * 4);
const reach = 12.0 + hash(i, 41) * 3.0;
const start = 7.0;
const c = bark[1 + (i % 4)];
line(cx + Math.cos(a) * start, cy + Math.sin(a) * start, 5,
cx + Math.cos(a) * reach, cy + Math.sin(a) * reach, 1, 2.1, c);
for (let t = 7; t <= reach; t += 1.5) {
const x = Math.round(cx + Math.cos(a) * t);
const y = Math.round(cy + Math.sin(a) * t);
const h = Math.max(1, Math.round(5 - (t - 7) * 0.75));
box(x, y, 0, x + 1, y + 1, h,
bark[Math.min(4, 1 + Math.floor(hash(i, t) * 4))]);
if (hash(i, t, 7) > 0.43)
set(x, y, h + 1, "#ae7040");
}
}
// Rough overhanging tabletop: a thick slice of the same tree.
function radius(a) {
return 18.25
+ 0.38 * Math.sin(9 * a + 0.5)
+ 0.27 * Math.sin(17 * a - 0.8)
+ 0.16 * Math.sin(31 * a);
}
for (let z = 29; z <= 33; z++) {
for (let x = 4; x <= 44; x++) for (let y = 4; y <= 44; y++) {
const dx = x - cx, dy = y - cy;
const r = Math.hypot(dx, dy);
const a = Math.atan2(dy, dx);
const lip = radius(a) - (z === 29 ? 0.7 : 0);
if (r > lip) continue;
const h = hash(x, y, Math.floor(z / 2));
let color;
if (r < lip - 2.3) {
color = z === 33 ? "#c88949" : "#794825";
} else {
color = z === 33 ? (h < 0.5 ? "#ab6834" : "#bd773b")
: h < 0.18 ? "#59351f"
: h < 0.52 ? "#754326"
: h < 0.82 ? "#8f522b" : "#a66432";
}
set(x, y, z, color);
}
}
// Irregular vertical notches around the bark rim.
for (let i = 0; i < 94; i++) {
const a = i * Math.PI * 2 / 94;
const r = radius(a);
const x = Math.round(cx + Math.cos(a) * (r - 0.3));
const y = Math.round(cy + Math.sin(a) * (r - 0.3));
const bottom = 29 + Math.floor(hash(i, 12) * 3);
for (let z = bottom; z <= 32; z++)
set(x, y, z, hash(i, z) < 0.22 ? "#5b361f" :
hash(i, 8) > 0.57 ? "#a46232" : "#754326");
}
// Fresh-cut golden top, with a thin darker live edge.
for (let x = 4; x <= 44; x++) for (let y = 4; y <= 44; y++) {
const dx = x - cx, dy = y - cy;
const r = Math.hypot(dx, dy);
const a = Math.atan2(dy, dx);
const edge = radius(a);
if (r > edge) continue;
const h = hash(x, y, 13);
let color;
if (r > edge - 0.72)
color = h < 0.28 ? "#825029" : h < 0.72 ? "#a46532" : "#c48846";
else if (r > edge - 1.55)
color = h < 0.35 ? "#dfa458" : "#f4c778";
else {
color = grain[Math.floor(h * grain.length)];
// Broken, gently wandering growth rings rather than perfect circles.
const warped = r + 0.43 * Math.sin(a * 6 + r * 0.55)
+ 0.22 * Math.sin(a * 13);
const rings = [3.8, 6.7, 9.5, 12.1, 14.6, 16.2];
for (const ring of rings) {
if (Math.abs(warped - ring) < 0.48 && hash(x, y, 29) > 0.38) {
color = h > 0.64 ? "#e3a651" : "#eab361";
break;
}
}
if (r < 2.5 && h > 0.40) color = "#edb665";
if (h > 0.965) color = "#e6aa58";
}
set(x, y, 34, color);
}
// Small dark knot near the outer edge of the cut face.
for (let x = 35; x <= 39; x++) for (let y = 20; y <= 24; y++) {
const d = Math.hypot((x - 37) * 1.2, y - 22);
if (d < 1.65 && get(x, y, 34))
set(x, y, 34, hash(x, y) > 0.35 ? "#9e6335" : "#b87a3e");
}import { draw } from "./assets/voxel";
import { mesaTronco } from "./assets/mesa-tronco";
// En cada cuadro: parado sobre (x, y), el centro de su base.
draw(ctx, mesaTronco, 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 mesa-tronco
# Con las herramientas de Fogón (conector)
add_assets({ slug: "<tu-juego>", ids: ["mesa-tronco"] })
# Queda en client/assets/mesa-tronco.ts
# https://fogon.page/object/mesa-tronco