← Medieval3D assetfogon/molino-de-agua
Molino de agua
Drag to turn it// «Molino de agua» · gpt-6-sol (medium)
// A small watermill seen from the front with its WATER WHEEL in clear view on the FRONT-RIGHT side (facing the viewer at -y or at +x, never hidden behind the house): a big round wooden water wheel almost as tall as the house, made of two rings of dark brown wood connected by many flat paddles around the rim and spokes to a central hub on a horizontal axle going into the wall, its lower part dipping into a strip of blue stream water running past the house. The house: stone ground floor, cream plaster upper floor with dark timber beams, red tiled roof, small wooden door and windows. Make the wheel the most noticeable thing.
const stone = ["#938d80", "#a8a294", "#827d72", "#b8b1a2"];
const plaster = ["#e9d9b5", "#f1e2c1", "#ddcba6"];
const timber = "#493022";
const timberLight = "#68422b";
const iron = "#292b2a";
const red = ["#a84635", "#ba5740", "#923b33", "#c36849"];
const water = ["#326f94", "#397fa4", "#286384", "#468dad"];
const sx0 = 5, sx1 = 25, sy0 = 14, sy1 = 35;
// A long, shallow millrace beside the house.
box(27, 2, 0, 46, 43, 1, "#355d70");
for (let x = 28; x <= 45; x++)
for (let y = 3; y <= 42; y++) {
set(x, y, 1, water[Math.floor(rand() * water.length)]);
if (rand() < 0.13) set(x, y, 2, rand() < 0.5 ? "#6cb4c4" : "#91c6c8");
}
for (let y = 2; y <= 43; y++) {
for (let z = 0; z <= 2; z++) {
set(26, y, z, stone[(y + z) % 3]);
set(47, y, z, stone[(y + z + 1) % 3]);
}
}
for (let y = 4; y < 43; y += 5) {
line(29, y, 2, 32, y + 1, 2, 0, "#82c3cd");
line(42, y + 2, 2, 44, y + 2, 2, 0, "#72b3c4");
}
// Solid stone ground floor, with individually shaded surface stones.
box(sx0, sy0, 0, sx1, sy1, 14, "#928c80");
for (let z = 0; z <= 14; z++)
for (let x = sx0; x <= sx1; x++)
for (let y = sy0; y <= sy1; y++)
if (x === sx0 || x === sx1 || y === sy0 || y === sy1) {
let c = stone[Math.floor(rand() * stone.length)];
if (rand() < 0.035 && z < 6) c = "#68775a";
set(x, y, z, c);
}
for (let z = 2; z <= 13; z += 4)
for (let x = 6; x <= 24; x++)
if (x % 5 !== 0) set(x, 13, z, "#777267");
for (let z = 3; z <= 12; z += 5)
for (let y = 16; y <= 34; y++)
if (y % 5 !== 0) set(26, y, z, "#797367");
box(4, 13, 0, 26, 36, 1, "#79766b");
box(4, 13, 14, 26, 36, 15, "#514032");
// Cream upper storey.
box(sx0, sy0, 16, sx1, sy1, 29, plaster[0]);
for (let x = sx0; x <= sx1; x++)
for (let y = sy0; y <= sy1; y++)
for (let z = 16; z <= 29; z++)
if ((x === sx0 || x === sx1 || y === sy0 || y === sy1) && rand() < 0.26)
set(x, y, z, plaster[Math.floor(rand() * plaster.length)]);
// Plastered front gable beneath the pitched roof.
for (let x = 5; x <= 25; x++)
for (let z = 30; z < 40 - Math.abs(x - 15); z++)
set(x, 14, z, plaster[Math.floor(rand() * plaster.length)]);
// Heavy exposed timber framing.
for (const y of [13, 36]) {
for (const x of [5, 15, 25]) box(x, y, 15, x, y, 29, timber);
box(5, y, 15, 25, y, 16, timber);
box(5, y, 28, 25, y, 29, timber);
line(5, y, 16, 11, y, 27, 0, timberLight);
line(25, y, 16, 19, y, 27, 0, timberLight);
}
for (const x of [4, 26]) {
for (const y of [14, 24, 35]) box(x, y, 15, x, y, 29, timber);
box(x, 14, 15, x, 35, 16, timber);
box(x, 14, 28, x, 35, 29, timber);
}
for (let x = 7; x <= 23; x += 8)
line(x, 13, 30, x + 6, 13, Math.min(37, 36 + (x === 7 ? 1 : -1)), 0, timber);
box(14, 13, 30, 16, 13, 37, timber);
// Chunky red-clay roof, its overlapping tile courses visible at the front.
for (let x = 3; x <= 27; x++) {
let top = 40 - Math.abs(x - 15);
for (let y = 12; y <= 37; y++) {
let c = red[(Math.floor(y / 3) + Math.floor(x / 3)) % red.length];
if (rand() < 0.11) c = red[Math.floor(rand() * red.length)];
set(x, y, top - 1, "#71382e");
set(x, y, top, c);
if (y % 4 === 0) set(x, y, top + 1, "#ce7351");
}
}
for (let y = 11; y <= 38; y++) box(14, y, 40, 16, y, 40, "#793d30");
for (let x = 3; x <= 27; x++) {
let z = 40 - Math.abs(x - 15);
set(x, 11, z, "#593428");
if (x % 3 === 0) set(x, 11, z + 1, "#d27a53");
}
// Front doorway, stone arch, iron-strapped oak door.
box(12, 12, 0, 19, 14, 11, "#625d55");
box(13, 11, 0, 18, 11, 10, "#70452b");
for (let x = 13; x <= 18; x++)
if (x % 2 === 0) box(x, 10, 0, x, 10, 10, "#815333");
box(12, 10, 0, 12, 12, 11, "#b1a99a");
box(19, 10, 0, 19, 12, 11, "#b1a99a");
box(12, 10, 11, 19, 12, 12, "#aaa293");
for (let z of [3, 8]) {
box(13, 9, z, 18, 9, z, iron);
set(13, 8, z, "#aaa18b");
set(18, 8, z, "#aaa18b");
}
set(17, 8, 5, "#d5aa56");
// Deep-set, leaded windows.
function frontWindow(x, z, w, h) {
box(x - 1, 12, z - 1, x + w, 13, z + h, timber);
box(x, 11, z, x + w - 1, 11, z + h - 1, "#27343d");
box(x, 10, z, x + w - 1, 10, z + h - 1, "#779ba0");
for (let xx = x; xx < x + w; xx++)
for (let zz = z; zz < z + h; zz++)
if ((xx + zz) % 4 === 0) set(xx, 9, zz, "#a9b7a4");
box(x + Math.floor(w / 2), 9, z, x + Math.floor(w / 2), 9, z + h - 1, iron);
box(x, 9, z + Math.floor(h / 2), x + w - 1, 9, z + Math.floor(h / 2), iron);
box(x - 1, 10, z - 1, x + w, 12, z - 1, "#b2aa99");
}
frontWindow(7, 6, 3, 5);
frontWindow(21, 6, 3, 5);
frontWindow(8, 20, 4, 5);
frontWindow(19, 20, 4, 5);
// Small heraldic sign above the doorway.
box(13, 10, 31, 17, 10, 34, "#b8924b");
box(14, 9, 31, 16, 9, 33, "#922f39");
box(15, 8, 31, 15, 8, 33, "#d9ad55");
box(14, 8, 32, 16, 8, 32, "#d9ad55");
// The horizontal axle enters the right-hand wall.
cylinder(24, 20, 17, 2, 19, timber, "x");
cylinder(26, 20, 17, 3, 2, iron, "x");
for (let x of [27, 29, 39, 41])
cylinder(x, 20, 17, 2.4, 1, "#292a29", "x");
// Everything from here is one rotating water wheel.
part("water wheel", [35, 20, 17]);
const cy = 20, cz = 17, radius = 16;
const N = 12;
// Broad wooden spokes on both faces, with a sturdy central hub.
for (let face of [31, 40]) {
for (let i = 0; i < N; i++) {
let a = i * Math.PI * 2 / N;
line(face, cy + Math.sin(a) * 2, cz + Math.cos(a) * 2,
face, cy + Math.sin(a) * 15, cz + Math.cos(a) * 15,
1, i % 2 ? "#694127" : "#794b2d");
line(face + 1, cy + Math.sin(a) * 4, cz + Math.cos(a) * 4,
face + 1, cy + Math.sin(a) * 13, cz + Math.cos(a) * 13,
0, "#ab7746");
}
}
cylinder(30, cy, cz, 3, 13, "#6c4027", "x");
for (let x of [30, 41]) {
cylinder(x, cy, cz, 3, 1, "#9b683c", "x");
cylinder(x + 1, cy, cz, 1, 1, iron, "x");
}
// Twelve long, flat buckets bridge the two rims.
for (let i = 0; i < N; i++) {
let a = i * Math.PI * 2 / N;
for (let t = -3; t <= 3; t++) {
let yy = cy + Math.sin(a) * radius + Math.cos(a) * t;
let zz = cz + Math.cos(a) * radius - Math.sin(a) * t;
line(32, yy, zz, 40, yy, zz, 0, t % 3 === 0 ? "#ae7948" : "#785031");
}
for (let t of [-2, 2]) {
let yy = cy + Math.sin(a) * radius + Math.cos(a) * t;
let zz = cz + Math.cos(a) * radius - Math.sin(a) * t;
set(35, yy, zz, "#bc8753");
}
}
// Two thick continuous circular rims, with brighter outer edges.
for (let x of [30, 31, 40, 41]) {
for (let j = 0; j < 720; j++) {
let a = j * Math.PI / 360;
for (let r = 14; r <= 16; r++) {
let yy = cy + Math.sin(a) * r;
let zz = cz + Math.cos(a) * r;
set(x, yy, zz, r === 16 ? "#4a2d20" :
r === 14 ? "#a37143" : "#684026");
}
}
}
// Iron straps and bright rivets punctuate the visible outer face.
for (let i = 0; i < N; i++) {
let a = i * Math.PI * 2 / N;
let yy = cy + Math.sin(a) * 15;
let zz = cz + Math.cos(a) * 15;
line(42, yy - Math.sin(a), zz - Math.cos(a),
42, yy + Math.sin(a), zz + Math.cos(a), 0, iron);
set(43, yy, zz, "#b4a488");
}
animate("water wheel", { type: "spin", axis: "x", speed: 1.5 });
part("body", [0, 0, 0]);import { draw } from "./assets/voxel";
import { molinoDeAgua } from "./assets/molino-de-agua";
// Every frame: standing on (x, y), the centre of its base.
draw(ctx, molinoDeAgua, 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 molino-de-agua
# With Fogón's tools (connector)
add_assets({ slug: "<your-game>", ids: ["molino-de-agua"] })
# It lands in client/assets/molino-de-agua.ts
# https://fogon.page/object/molino-de-agua