← Ancient and prehistory3D assetfogon/domed-house
Casa con cúpula
Drag to turn it// «Casa con cúpula» · propio
// A whitewashed desert house with a parapet and a big white dome on its flat roof topped by a small dark iron ball, an arched red door and a striped cloth awning over it on two poles.
// A whitewashed desert house with a parapet and a big white dome on its flat roof topped by a dark iron ball, an
// arched red door and a striped cloth awning over it on two poles.
const white = ["#cfc4aa", "#ddd3bb", "#e8dfca", "#f1ead9", "#f9f4e8"];
const foot = ["#9c8a66", "#ab9973", "#b9a781"];
const red = ["#7d2a1c", "#96372a", "#ad4634", "#c25742"];
const yellow = ["#b98626", "#cf9a35", "#e0ad45", "#edc05e"];
const cream = ["#cbb68c", "#dcc9a0", "#ead9b6", "#f5e8cb"];
const pole = ["#3b2718", "#4a321f", "#5a3e27"];
const iron = ["#232221", "#33312f", "#4a4744"];
const dark = "#1a120c";
const clampi = (v, lo, hi) => Math.max(lo, Math.min(hi, v));
const X0 = 6, X1 = 41, Y0 = 16, Y1 = 42, ROOF = 21;
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);
};
function wash(x, y, z) {
let tone = 2 + (hash(Math.floor((x + y) / 6), Math.floor(z / 5)) > 0.6 ? 1 : 0) - (hash(Math.floor((x + y) / 4), Math.floor(z / 3), 2) > 0.84 ? 1 : 0);
if (z > 15) tone++;
if (z < 6) tone--;
if (rand() < 0.06) tone--;
return white[clampi(tone, 0, 4)];
}
// Walls (hollow), roof and parapet.
for (let x = X0; x <= X1; x++)
for (let y = Y0; y <= Y1; y++)
for (let z = 0; z <= ROOF + 3; z++) {
const wall = x <= X0 + 1 || x >= X1 - 1 || y <= Y0 + 1 || y >= Y1 - 1;
if (z > ROOF) {
if (wall) set(x, y, z, z === ROOF + 3 ? white[clampi(4 - (rand() < 0.15 ? 1 : 0), 0, 4)] : wash(x, y, z + 4));
} else if (wall) set(x, y, z, wash(x, y, z));
else if (z >= ROOF - 1) set(x, y, z, hash(x >> 2, y >> 2) > 0.5 ? "#d8cdb4" : "#cdc2a8");
}
// A sand-coloured foot.
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 <= 3; z++) set(x, y, z, z === 3 ? foot[2] : foot[clampi(1 - (rand() < 0.2 ? 1 : 0) + ((x + y) % 7 === 0 ? -1 : 0), 0, 2)]);
}
// The dome: a shell on the roof, with a ring at its base and the iron ball on a stem.
const CX = 23.5, CY = 29, RX = 11.5, RZ = 10.5;
for (let x = 10; x <= 37; x++)
for (let y = 16; y <= 42; y++)
for (let z = ROOF + 1; z <= ROOF + 12; z++) {
const nx = (x - CX) / RX, ny = (y - CY) / RX, nz = (z - ROOF - 1) / RZ;
const d = Math.hypot(nx, ny, nz);
if (d > 1 || d < 0.8) continue;
let tone = 2 + (nz > 0.55 ? 1 : 0) + (-nx - ny > 0.5 ? 1 : 0) - (nx + ny > 0.7 ? 1 : 0) - (nz < 0.12 ? 1 : 0);
if (rand() < 0.05) tone--;
set(x, y, z, white[clampi(tone, 0, 4)]);
}
for (let x = 10; x <= 37; x++)
for (let y = 16; y <= 42; y++) {
const d = Math.hypot(x - CX, y - CY);
if (d <= RX + 1.2 && d >= RX - 1.5) set(x, y, ROOF + 1, "#cfc4aa");
}
box(23, 28, ROOF + 11, 24, 29, ROOF + 13, iron[1]);
sphere(23.5, 28.5, ROOF + 15, 1.6, iron[1]);
set(23, 28, ROOF + 16, iron[2]);
// The door: arched, red, in a sand-coloured frame.
const DX = 23.5, SPRING = 10, DR = 5;
for (let x = 15; x <= 32; x++)
for (let z = 0; z <= 18; z++) {
const e = z <= SPRING ? Math.abs(x - DX) - DR : Math.hypot(x - DX, z - SPRING) - DR;
if (e <= 0) {
const seam = x === 23 || x === 24;
const fan = z > SPRING;
let tone = 2 + (x < DX ? 1 : 0) - (Math.round(x - DX + 20.5) % 3 === 0 ? 1 : 0) - (z < 3 ? 1 : 0);
if (fan) tone = Math.round(Math.atan2(z - SPRING, x - DX) * 3) % 2 === 0 ? 1 : 2;
set(x, Y0 - 1, z, seam && !fan && z > 0 ? (x === 23 ? red[0] : red[1]) : red[clampi(tone, 0, 3)]);
set(x, Y0, z, dark);
if (z === SPRING) set(x, Y0 - 1, z, red[0]);
} else if (e <= 2) {
for (const y of [Y0 - 2, Y0 - 1]) set(x, y, z, foot[clampi(1 + (y === Y0 - 2 ? 1 : 0) - (z % 4 === 3 ? 1 : 0), 0, 2)]);
}
}
set(21, Y0 - 2, 7, "#c9a23a");
set(26, Y0 - 2, 7, "#c9a23a");
box(16, Y0 - 3, 0, 31, Y0 - 3, 0, foot[2]);
// Two small arched windows, one each side of the door, and one on the left wall.
for (const wx of [10, 36]) {
box(wx, Y0, 9, wx + 2, Y0, 14, dark);
set(wx + 1, Y0, 15, dark);
box(wx - 1, Y0 - 1, 8, wx + 3, Y0 - 1, 8, foot[2]);
}
box(X0, 27, 9, X0, 30, 14, dark);
box(X0, 28, 15, X0, 29, 15, dark);
box(X0 - 1, 26, 8, X0 - 1, 31, 8, foot[2]);
// The awning: striped cloth from the wall down to two poles, with a hem.
const A0 = 13, A1 = 34;
for (let x = A0; x <= A1; x++) {
const s = Math.floor((x - A0) / 3) % 2 === 0;
const tones = s ? yellow : cream;
const fold = (x - A0) % 3 === 0 ? 1 : 0;
for (let y = 5; y <= Y0 - 1; y++) {
const z = Math.round(15 + (y - 5) * 0.48 - 1.1 * Math.sin(((x - A0) / (A1 - A0)) * Math.PI));
set(x, y, z, tones[clampi(3 - fold - (rand() < 0.1 ? 1 : 0), 0, 3)]);
set(x, y, z - 1, tones[clampi(1 - fold, 0, 3)]);
}
const zf = Math.round(15 - 1.1 * Math.sin(((x - A0) / (A1 - A0)) * Math.PI));
const hang = Math.abs(((x - A0) % 6) - 3) < 1.5 ? 3 : 2;
for (let k = 1; k <= hang; k++) set(x, 5, zf - k, tones[clampi(2 - fold - (k === hang ? 1 : 0), 0, 3)]);
}
for (const px of [A0, A1 - 1]) {
for (let z = 0; z <= 15; z++) for (let i = 0; i < 2; i++) for (let j = 0; j < 2; j++) set(px + i, 5 + j, z, pole[clampi(1 + (i === 0 && j === 0 ? 1 : 0) - (z < 2 ? 1 : 0), 0, 2)]);
}import { draw } from "./assets/voxel";
import { domedHouse } from "./assets/domed-house";
// Every frame: standing on (x, y), the centre of its base.
draw(ctx, domedHouse, 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 domed-house
# With Fogón's tools (connector)
add_assets({ slug: "<your-game>", ids: ["domed-house"] })
# It lands in client/assets/domed-house.ts
# https://fogon.gg/object/domed-house