← Antiguo y prehistoriaObjeto 3Dfogon/desert-house
Casa del zoco
Arrastrá para girarlo// «Casa del zoco» · propio
// A flat-roofed desert town house in ochre plaster: a darker stone foot, a pale band and a low parapet at the top, an arched double door painted blue with a pale stone arch, a window with green shutters, and round wooden beam ends sticking out under the roof.
// A flat-roofed desert town house in ochre plaster: a darker stone foot, a pale band and a low parapet, an arched
// blue double door in a pale stone arch, a window with green shutters and round beam ends under the roof.
const plaster = ["#b98848", "#c99a55", "#d6aa68", "#dfb676", "#e8c389"];
const foot = ["#806038", "#8f6d42", "#9c794c", "#a98658"];
const trim = ["#d4bd8c", "#e0cb9c", "#ebd8ad", "#f4e6c4"];
const blue = ["#22566f", "#2f6f8f", "#3d84a6", "#52a0c2"];
const green = ["#2f5f3d", "#3f7d4f", "#4f9460"];
const beam = ["#4a321f", "#5a3e27", "#6d4c30"];
const dark = "#1a120c";
const clampi = (v, lo, hi) => Math.max(lo, Math.min(hi, v));
const X0 = 5, X1 = 42, Y0 = 13, Y1 = 38, ROOF = 27;
const hash = (a, b, c = 0) => {
const s = Math.sin(a * 12.9898 + b * 78.233 + c * 37.719) * 43758.5453;
return s - Math.floor(s);
};
// Plaster: big uneven patches, lighter towards the top.
function plasterAt(x, y, z) {
let tone = 2 + (hash(Math.floor((x + y) / 6), Math.floor(z / 5)) > 0.62 ? 1 : 0) - (hash(Math.floor((x + y) / 4), Math.floor(z / 3), 2) > 0.8 ? 1 : 0);
if (z > 20) tone++;
if (z < 8) tone--;
if (x === X0) tone++;
if (rand() < 0.07) tone--;
return plaster[clampi(tone, 0, 4)];
}
// The walls (hollow) and the roof.
for (let x = X0; x <= X1; x++)
for (let y = Y0; y <= Y1; y++)
for (let z = 0; z <= ROOF; z++) {
const wall = x <= X0 + 1 || x >= X1 - 1 || y <= Y0 + 1 || y >= Y1 - 1;
if (wall) set(x, y, z, plasterAt(x, y, z));
else if (z >= ROOF - 1) set(x, y, z, hash(x >> 2, y >> 2) > 0.5 ? "#c7a46a" : "#bb9860");
}
// The foot: stone, a block wider.
for (let x = X0 - 1; x <= X1 + 1; x++)
for (let y = Y0 - 1; y <= Y1 + 1; y++) {
if (x > X0 && x < X1 && y > Y0 && y < Y1) continue;
for (let z = 0; z <= 4; z++) {
const u = x < X0 || x > X1 ? y : x;
const line = z === 2 || (u + (z > 2 ? 4 : 0)) % 8 === 0;
set(x, y, z, line ? "#6d5230" : foot[clampi(1 + Math.floor(hash(Math.floor((u + (z > 2 ? 4 : 0)) / 8), z > 2 ? 1 : 0) * 3) - (rand() < 0.1 ? 1 : 0), 0, 3)]);
}
}
// The pale band and the parapet above it.
for (let x = X0 - 1; x <= X1 + 1; x++)
for (let y = Y0 - 1; y <= Y1 + 1; y++) {
const edge = x <= X0 || x >= X1 || y <= Y0 || y >= Y1;
if (!edge) continue;
for (let z = ROOF - 2; z <= ROOF - 1; z++) set(x, y, z, trim[clampi((z === ROOF - 1 ? 2 : 1) + (x < X0 ? 1 : 0) - (rand() < 0.12 ? 1 : 0), 0, 3)]);
}
for (let x = X0; x <= X1; x++)
for (let y = Y0; y <= Y1; y++) {
if (!(x <= X0 + 1 || x >= X1 - 1 || y <= Y0 + 1 || y >= Y1 - 1)) continue;
for (let z = ROOF + 1; z <= ROOF + 4; z++) set(x, y, z, z === ROOF + 4 ? trim[clampi(2 - (rand() < 0.15 ? 1 : 0), 0, 3)] : plasterAt(x, y, z + 3));
}
// The door: a pale stone arch on two jambs, two blue leaves and a fan over them.
const DX = 17, SPRING = 13, DR = 5.5;
for (let x = 8; x <= 26; x++)
for (let z = 0; z <= 21; z++) {
const inside = z <= SPRING ? Math.abs(x - DX) <= DR : Math.hypot(x - DX, z - SPRING) <= DR;
const e = z <= SPRING ? Math.abs(x - DX) - DR : Math.hypot(x - DX, z - SPRING) - DR;
if (inside) {
const seam = x === DX;
const fan = z > SPRING;
const ray = fan && Math.round(Math.atan2(z - SPRING, x - DX) * 3.2) % 2 === 0;
let tone = 2 + (x < DX ? 1 : 0) - ((x - DX + 20) % 3 === 0 ? 1 : 0) - (z < 3 ? 1 : 0);
if (fan) tone = ray ? 1 : 2;
set(x, Y0 - 1, z, seam && !fan ? dark : blue[clampi(tone, 0, 3)]);
set(x, Y0, z, dark);
if (z === SPRING) set(x, Y0 - 1, z, blue[0]);
} else if (e <= 2.2) {
const joint = z <= SPRING ? z % 4 === 3 : Math.round(Math.atan2(z - SPRING, x - DX) * 4) % 2 === 0;
for (const y of [Y0 - 2, Y0 - 1]) set(x, y, z, trim[clampi((joint ? 1 : 2) + (y === Y0 - 2 ? 1 : 0) - (rand() < 0.1 ? 1 : 0), 0, 3)]);
}
}
// Knobs, and a step.
set(DX - 2, Y0 - 2, 8, "#c9a23a");
set(DX + 2, Y0 - 2, 8, "#c9a23a");
box(8, Y0 - 4, 0, 26, Y0 - 3, 0, trim[2]);
box(9, Y0 - 3, 1, 25, Y0 - 3, 1, trim[1]);
// The window: a dark opening, a sill and a lintel, and green shutters open to each side.
const WX0 = 31, WX1 = 35, WZ0 = 12, WZ1 = 20;
box(WX0, Y0 - 1, WZ0, WX1, Y0, WZ1, dark);
box(WX0 - 1, Y0 - 1, WZ1 + 1, WX1 + 1, Y0 - 1, WZ1 + 1, trim[2]);
box(WX0 - 2, Y0 - 2, WZ0 - 1, WX1 + 2, Y0 - 1, WZ0 - 1, trim[3]);
for (let z = WZ0; z <= WZ1; z++) {
for (const [x0, x1] of [[WX0 - 4, WX0 - 1], [WX1 + 1, WX1 + 4]])
for (let x = x0; x <= x1; x++) set(x, Y0 - 2, z, green[clampi((z % 2 ? 2 : 1) - (x === x0 || x === x1 ? 1 : 0), 0, 2)]);
}
box(33, Y0 - 1, WZ0, 33, Y0 - 1, WZ1, "#3a2a1c");
box(WX0, Y0 - 1, 16, WX1, Y0 - 1, 16, "#3a2a1c");
// Round beam ends sticking out under the band, front and left side.
for (const x of [8, 14, 20, 26, 32, 38]) {
cylinder(x, Y0 - 3, 23, 1, 3, beam[1], "y");
set(x, Y0 - 3, 23, beam[2]);
set(x, Y0 - 3, 24, beam[2]);
}
for (const y of [17, 23, 29, 35]) {
cylinder(X0 - 3, y, 23, 1, 3, beam[1], "x");
set(X0 - 3, y, 23, beam[2]);
set(X0 - 3, y, 24, beam[2]);
}
// A small window on the left wall, with its sill.
box(X0, 24, 12, X0, 28, 18, dark);
box(X0 - 1, 23, 11, X0 - 1, 29, 11, trim[3]);
box(X0 - 1, 23, 19, X0 - 1, 29, 19, trim[2]);
box(X0 - 1, 26, 12, X0 - 1, 26, 18, "#3a2a1c");import { draw } from "./assets/voxel";
import { desertHouse } from "./assets/desert-house";
// En cada cuadro: parado sobre (x, y), el centro de su base.
draw(ctx, desertHouse, 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 desert-house
# Con las herramientas de Fogón (conector)
add_assets({ slug: "<tu-juego>", ids: ["desert-house"] })
# Queda en client/assets/desert-house.ts
# https://fogon.gg/object/desert-house