← Asian3D assetfogon/castillo-japones
Castillo japonés
Drag to turn it// «Castillo japonés» · gpt-6-sol (medium)
// A Japanese castle (like Himeji): a tall steep grey stone base with sloped walls, and on top a multi-tiered white keep with three to four layers of dark grey tiled roofs with curved gables, small dark windows, and two golden fish ornaments on the very top ridge.
const stone = ["#777b79", "#898d89", "#9b9e98", "#686e6c"];
const white = "#f2ede0";
const shadowWhite = "#d9d9ce";
const red = "#813b32";
const dark = "#202b2c";
const gold = "#d7a94c";
function block(x, y, z, c) {
if (z >= 0 && z < 48) set(x, y, z, c);
}
// Steep, battered stone foundation.
for (let z = 0; z <= 11; z++) {
const inset = Math.floor(z * 0.55);
const lo = 4 + inset, hi = 43 - inset;
for (let x = lo; x <= hi; x++)
for (let y = lo; y <= hi; y++) {
let c = stone[z > 8 ? 2 : z < 3 ? 3 : 1];
if (rand() < 0.16) c = stone[Math.floor(rand() * stone.length)];
if ((x + 2 * z) % 7 === 0 && (y === lo || y === hi)) c = stone[0];
if ((y + 3 * z) % 8 === 0 && (x === lo || x === hi)) c = stone[0];
block(x, y, z, c);
}
// Fine horizontal courses on the visible front and sides.
if (z % 3 === 0) {
for (let x = lo; x <= hi; x++) block(x, lo, z, "#666d6b");
for (let y = lo; y <= hi; y++) block(lo, y, z, "#707775");
}
}
// Broad stone stairs rising toward the front gate.
for (let y = 2; y <= 13; y++) {
const height = Math.min(12, Math.floor((y - 2) * 1.08));
for (let x = 20; x <= 27; x++)
for (let z = 0; z <= height; z++)
block(x, y, z, z === height ? "#b6b5aa" : "#868a85");
block(19, y, Math.max(0, height - 1), "#777b76");
block(28, y, Math.max(0, height - 1), "#777b76");
}
// White plaster walls with red-lacquered framing and small barred windows.
function keep(x0, y0, x1, y1, z0, z1, windowHeight) {
box(x0, y0, z0, x1, y1, z1, shadowWhite);
box(x0 + 1, y0, z0 + 1, x1 - 1, y0, z1 - 1, white);
box(x0 + 1, y1, z0 + 1, x1 - 1, y1, z1 - 1, white);
box(x0, y0 + 1, z0 + 1, x0, y1 - 1, z1 - 1, white);
box(x1, y0 + 1, z0 + 1, x1, y1 - 1, z1 - 1, white);
for (const z of [z0 + 1, z1 - 1]) {
box(x0, y0, z, x1, y0, z, red);
box(x0, y1, z, x1, y1, z, red);
box(x0, y0, z, x0, y1, z, red);
box(x1, y0, z, x1, y1, z, red);
}
for (const x of [x0, x1, Math.round((x0 + x1) / 2)]) {
box(x, y0, z0 + 1, x, y0, z1 - 1, red);
box(x, y1, z0 + 1, x, y1, z1 - 1, red);
}
const wz = z0 + windowHeight;
const xs = [];
for (let x = x0 + 3; x <= x1 - 3; x += 5) xs.push(x);
for (const x of xs)
for (const y of [y0, y1]) {
box(x, y - (y === y0 ? 1 : 0), wz,
x + 1, y + (y === y1 ? 1 : 0), wz + 2, dark);
block(x, y + (y === y0 ? -1 : 1), wz + 2, "#a77540");
block(x, y + (y === y0 ? -1 : 1), wz, "#a77540");
}
for (let y = y0 + 4; y <= y1 - 4; y += 6)
for (const x of [x0 - 1, x1 + 1]) {
box(x, y, wz, x, y + 1, wz + 2, dark);
block(x, y, wz + 2, gold);
}
}
// Sweeping tiled roof: pitched toward the viewer, with curled-up corners.
function roof(cx, cy, eave, halfX, halfY, rise) {
const surface = (x, y) => {
const slope = Math.round(rise * (1 - Math.abs(y - cy) / halfY));
const corner = Math.abs(x - cx) >= halfX - 2 &&
Math.abs(y - cy) >= halfY - 2 ? 2 : 0;
return eave + slope + corner;
};
for (let x = cx - halfX; x <= cx + halfX; x++)
for (let y = cy - halfY; y <= cy + halfY; y++) {
const z = surface(x, y);
block(x, y, z - 2, "#283132");
block(x, y, z - 1, "#303b3b");
let tile = (x + y) % 4 === 0 ? "#53605d" : "#394747";
if (rand() < 0.11) tile = "#60706a";
block(x, y, z, tile);
if (y === cy - halfY || y === cy + halfY)
block(x, y, z - 1, "#202b2c");
}
// Raised ridge, edged in warm gold.
for (let x = cx - halfX + 2; x <= cx + halfX - 2; x++) {
block(x, cy, eave + rise + 1, "#252f30");
if (x % 3 === 0) block(x, cy, eave + rise + 1, "#a18b57");
}
// Curved eave tips and dark tile ridges.
for (const x of [cx - halfX, cx + halfX])
for (const y of [cy - halfY, cy + halfY]) {
block(x, y, surface(x, y) + 1, "#293535");
block(x, y, surface(x, y) + 2, "#45524e");
}
// A prominent white triangular karahafu-style front gable.
const gy = cy - halfY - 1;
const gw = Math.min(7, halfX - 2);
for (let dx = -gw; dx <= gw; dx++) {
const top = eave + 1 + Math.round((rise + 1) * (1 - Math.abs(dx) / gw));
for (let z = eave + 1; z < top; z++)
block(cx + dx, gy, z, dx % 5 === 0 ? shadowWhite : white);
block(cx + dx, gy, top, "#303a3a");
}
block(cx, gy - 1, eave + rise, gold);
}
// Four ascending, progressively lighter-looking castle stories.
keep(12, 11, 35, 36, 12, 21, 3);
roof(24, 24, 19, 15, 16, 5);
keep(15, 14, 32, 33, 24, 30, 2);
roof(24, 24, 29, 12, 13, 4);
keep(18, 17, 29, 30, 33, 38, 2);
roof(24, 24, 37, 9, 10, 3);
keep(20, 19, 27, 28, 40, 43, 1);
roof(24, 24, 42, 6, 7, 2);
// Deep entrance beneath the lowest eave.
box(21, 10, 12, 26, 11, 18, "#272526");
box(21, 9, 12, 21, 10, 18, red);
box(26, 9, 12, 26, 10, 18, red);
box(21, 9, 19, 26, 10, 19, gold);
for (const x of [23, 25]) box(x, 9, 12, x, 9, 17, "#765044");
block(23, 8, 15, gold);
block(25, 8, 15, gold);
// Two gilded shachihoko fish, curled upward at the top ridge.
for (const side of [-1, 1]) {
const x = side < 0 ? 19 : 28;
block(x, 24, 45, "#bc8732");
block(x, 24, 46, gold);
block(x + side, 24, 46, "#f0c769"); // raised head
block(x + side, 24, 47, "#d7a94c"); // curling fin
block(x - side, 24, 46, "#e3b753"); // tail
block(x - side, 24, 47, gold);
block(x + side, 23, 46, "#9c6328"); // eye
}import { draw } from "./assets/voxel";
import { castilloJapones } from "./assets/castillo-japones";
// Every frame: standing on (x, y), the centre of its base.
draw(ctx, castilloJapones, 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 castillo-japones
# With Fogón's tools (connector)
add_assets({ slug: "<your-game>", ids: ["castillo-japones"] })
# It lands in client/assets/castillo-japones.ts
# https://fogon.page/object/castillo-japones