← Basics3D assetfogon/mesa-de-picnic
Mesa de picnic
Drag to turn it// «Mesa de picnic» · gpt-6-sol (medium)
// Picnic table («Mesa de picnic»).
// Picnic table: honey-colored timber, two benches, and a checked cloth.
const woodDark = "#805029";
const woodSide = "#a96d35";
const woodMid = "#c88743";
const woodTop = ["#e4a858", "#efb66a", "#df9e4c", "#f2bd70"];
function beam(x0, y0, z0, x1, y1, z1) {
box(x0, y0, z0, x1, y1, z1, woodDark);
box(x0, y0, z1, x1, y1, z1, woodMid);
if (x1 - x0 > y1 - y0)
box(x0, y0, z1, x1, y0, z1, "#d99a50");
}
function plank(x0, x1, y0, y1, z, shade) {
box(x0, y0, z - 2, x1, y1, z - 1, woodDark);
box(x0, y0, z - 1, x1, y1, z - 1, woodSide);
box(x0, y0, z, x1, y1, z, shade);
box(x0 + 1, y0 + 1, z, x1 - 1, y0 + 1, z, "#f5c477");
for (let i = 0; i < 13; i++) {
const x = x0 + 2 + Math.floor(rand() * (x1 - x0 - 3));
const y = y0 + Math.floor(rand() * (y1 - y0 + 1));
set(x, y, z, rand() < 0.55 ? "#bd793a" : "#f9cf82");
}
}
// Two pairs of splayed A-frame legs. Their stepped edges remain visibly chunky.
for (const x of [11, 36]) {
for (const side of [-1, 1]) {
for (let z = 0; z <= 24; z++) {
const y = Math.round(24.5 + side * (16 - z * 0.43));
box(x - 1, y - 1, z, x + 1, y + 1, z,
z % 5 === 0 ? "#ad7138" : woodSide);
set(x - 1, y - 1, z, "#ce8b45");
}
}
// Crosspiece under each pair of seats.
beam(x - 2, 10, 12, x + 2, 39, 14);
beam(x - 2, 18, 23, x + 2, 31, 25);
}
// Long central stretcher and short diagonal braces.
beam(11, 23, 11, 36, 26, 13);
for (const x of [13, 34]) {
line(x, 24, 13, x + (x < 24 ? 5 : -5), 24, 23, 1, "#a66a35");
line(x, 25, 14, x + (x < 24 ? 5 : -5), 25, 23, 0, "#d49549");
}
// Bench supports and seats, each made of two separate long boards.
for (const [ya, yb] of [[7, 12], [37, 42]]) {
for (const x of [11, 36])
beam(x - 3, ya + 1, 12, x + 3, yb - 1, 15);
plank(3, 44, ya, ya + 2, 17, woodTop[0]);
plank(3, 44, ya + 3, yb, 17, woodTop[1]);
box(5, ya + 2, 17, 42, ya + 2, 17, "#86552e");
}
// Heavy apron immediately beneath the tabletop.
beam(6, 17, 24, 41, 19, 26);
beam(6, 30, 24, 41, 32, 26);
for (const x of [9, 37])
beam(x, 18, 24, x + 2, 31, 26);
// Four lengthwise tabletop boards, with dark seams between them.
for (let i = 0; i < 4; i++) {
const y0 = 17 + i * 4;
plank(5, 42, y0, y0 + 2, 29, woodTop[i]);
box(6, y0 + 2, 29, 41, y0 + 2, 29, "#89562d");
}
box(5, 17, 27, 5, 31, 28, "#98602f");
box(42, 17, 27, 42, 31, 28, "#98602f");
// Occasional grain lines and small dark knots on the exposed wooden end.
for (let i = 0; i < 20; i++) {
const x = 7 + Math.floor(rand() * 22);
const y = 18 + Math.floor(rand() * 14);
if (get(x, y, 29) && get(x, y, 29) !== "#89562d")
set(x, y, 29, rand() < 0.6 ? "#bd7938" : "#f7cc7b");
}
for (const [x, y] of [[12, 20], [21, 27], [26, 18], [15, 30]]) {
set(x, y, 29, "#754b2b");
set(x + 1, y, 29, "#a56832");
}
// Red-and-cream checked tablecloth over one end of the table.
const cream = "#f8eddb", red = "#ce6960", pale = "#ebaaa0";
function clothColor(x, y) {
return (Math.floor((x - 30) / 3) + Math.floor((y - 16) / 3)) % 2
? cream : red;
}
for (let x = 30; x <= 43; x++)
for (let y = 16; y <= 33; y++)
set(x, y, 30, clothColor(x, y));
// Draped front and rear edges; the uneven hem reads as soft fabric.
for (let x = 30; x <= 43; x++) {
for (const y of [15, 34]) {
const hem = 22 + (x % 5 === 0 ? 2 : 0);
for (let z = hem; z <= 29; z++) {
const checked = (Math.floor((x - 30) / 3) +
Math.floor((30 - z) / 4) +
(y === 34 ? 1 : 0)) % 2;
set(x, y, z, checked ? cream : (z < 26 ? pale : red));
}
}
}
for (let y = 16; y <= 33; y++) {
const hem = 23 + (y % 6 === 0 ? 2 : 0);
for (let z = hem; z <= 29; z++)
set(44, y, z,
(Math.floor((y - 16) / 3) + Math.floor((30 - z) / 4)) % 2
? cream : pale);
}
box(30, 16, 30, 30, 33, 30, "#f5e7d4");
// Little dark teal drinking cup standing on the cloth.
cylinder(37, 24, 31, 2, 5, "#31575c");
cylinder(37, 24, 35, 2, 1, "#263f47");
set(37, 24, 36, "#74a4a7");
set(36, 24, 36, "#81b0b0");
set(37, 23, 36, "#557e84");
box(35, 23, 32, 35, 23, 34, "#628d91");
box(40, 24, 33, 41, 25, 35, "#35575d");
set(40, 24, 34, cream);import { draw } from "./assets/voxel";
import { mesaDePicnic } from "./assets/mesa-de-picnic";
// Every frame: standing on (x, y), the centre of its base.
draw(ctx, mesaDePicnic, x, y, { size: 64, angle: 0, t: performance.now() / 1000 });
// size: its longest side, in px
// angle: turn on the floor, in radians
// t: seconds; if it moves, that animates it# In a terminal, in the game's folder
fogon assets add mesa-de-picnic
# With Fogón's tools (connector)
add_assets({ slug: "<your-game>", ids: ["mesa-de-picnic"] })
# It lands in client/assets/mesa-de-picnic.ts
# https://fogon.page/object/mesa-de-picnic