← Back to Gallery

Loading 3D viewer…

Rock-hewn monolithic church (Lalibela) — indexed

by Jorge Barbero

Dimensions
96×40×96
Blocks
186,882
Compute Time
13.2s
Generation Time
3m 12s
Edits
1
Model
claude-opus-5
Created
August 29, 2026
autoevalindexedarchitecturerock-hewnethiopia
Prompt

User Prompt

an Ethiopian rock-hewn monolithic church in the Lalibela style: a cross-plan monolith of ochre-red volcanic tuff standing inside a deep rectangular trench cut into the bedrock, a rock-cut stairway descending from ground level into the trench courtyard, tiered flat roof carved with a cross motif, three registers of keyhole and ogee windows, pilaster relief on all faces; inside a pillared nave with carpets, candles and a stone altar

Enhanced Prompt

HeroBrain autoeval — condition=indexed. Retrieval via search_docs({action:"related"}) then read of what it returned: architecture-guide/9. Egyptian (battered walls, hypostyle hall, cavetto cornice, hieroglyph bands), organic-builds/Cliffs & Rock Faces (strata bands, stairs+buttons as rock protrusions). DIRECT process, palettes passed straight into fill, L-shaped rock-cut descent with a landing.

Photos

Monolito en la trinchera
Monolito en la trincheraexterior
Meseta y trinchera
Meseta y trincheraexterior
Nave hipóstila
Nave hipóstilainterior
Patio excavado
Patio excavadoexterior
CraftScript Source
// === BUILD PLAN ===
// Style: Ethiopian rock-hewn monolith. Knowledge routed by search_docs(related) ->
//        Egyptian (BATTERED walls, HYPOSTYLE hall, CAVETTO cornice, carved bands)
//        + Cliffs & Rock Faces (horizontal STRATA bands, stairs/buttons as rock protrusions).
// Footprint: LATIN CROSS - long nave x38..57 z30..68, transept x28..67 z40..53.
//            Free-standing inside a 61x53 trench cut 15 blocks into a red mesa.
// Height: court floor Y2 | body Y3..16 in THREE BATTERED TIERS (each inset 1) | roof Y17 + motif Y18
//         mesa top Y17, so the roof sits flush with the plateau (as at Bete Giyorgis).
//
// SECTION (looking west):
//   Y18  .......[+]cross motif[+].......
//   Y17  =======[ roof slab + cavetto ]=======   <- mesa level
//   Y14  |  tier 3 (inset 2)   |
//   Y9   |  tier 2 (inset 1)   |
//   Y3   |  tier 1 (base)      |
//   Y2   ~~~~~ courtyard floor ~~~~~
//
// HYPOSTYLE NAVE (top-down, 1 char = 2 blocks):
//   #############
//   #  O O   O  #   O = 2x2 monolithic column (Egyptian hypostyle: 2x2, spacing 3-4)
//   #  O O   O  #   A = altar bay (north end)
//   ##  crossing ##
//   #  O O   O  #
//   ######D######   D = door -> porch -> flight down to the courtyard
//
// MATERIALS PLAN (DIRECT - palettes passed straight into fill, no placeholders):
//   bedrock/trench : strata() position-palette, horizontal 3-block bands  - WHY: Cliffs doc, reads as CUT rock
//   body           : red_sandstone(40)+cut(25)+smooth(20)+terracotta(15) noise - WHY: one monolithic stone
//   carved bands   : chiseled_red_sandstone every tier top                - WHY: Egyptian hieroglyph band rule
//   cornice        : cavetto flare, red_sandstone_stairs upside-down      - WHY: Egyptian cavetto = wall_depth
//   interior       : smooth_red_sandstone floor, sandstone_wall columns, red/orange carpet, candles
// STAIR PLAN: L-shaped rock-cut descent, WEST side. flight A walk3->walk10 heading west (7),
//             landing 7x9 at walk10, flight B walk10->walk18 heading north (8). Porch: walk3->walk6.
// === END PLAN ===

var courtY = 2, courtWalk = 3;
var floorY = 5, walkY = 6, ceilY = 16;
var t1 = 3, t2 = 9, t3 = 14;      // tier bases
var roofY = 17, motifY = 18, mesaY = 17;

// ---- LATIN CROSS ----
var NX1 = 38, NX2 = 57, NZ1 = 30, NZ2 = 68;    // nave
var TX1 = 28, TX2 = 67, TZ1 = 40, TZ2 = 53;    // transept
function cross(x, z, g) {
    if (x >= NX1 - g && x <= NX2 + g && z >= NZ1 - g && z <= NZ2 + g) return true;
    if (x >= TX1 - g && x <= TX2 + g && z >= TZ1 - g && z <= TZ2 + g) return true;
    return false;
}
// interior (walls 4 thick, battered)
var INX1 = 42, INX2 = 53, INZ1 = 34, INZ2 = 64;
var ITX1 = 32, ITX2 = 63, ITZ1 = 44, ITZ2 = 49;
function inHall(x, z) {
    if (x >= INX1 && x <= INX2 && z >= INZ1 && z <= INZ2) return true;
    if (x >= ITX1 && x <= ITX2 && z >= ITZ1 && z <= ITZ2) return true;
    return false;
}

// ===== 1. MESA OF RED BEDROCK — strata bands (Cliffs & Rock Faces) =====
var STRATA = ['granite', 'terracotta', 'red_sandstone', 'brown_terracotta', 'stone', 'red_sandstone'];
function strata(x, y, z) {
    var wob = noise2d('perlin', x, z, { freq: 0.09, seed: 15 });
    var band = Math.floor((y + wob * 2.5 + 6) / 3) % STRATA.length;
    if (band < 0) band = 0;
    return STRATA[band];
}
var land = blob(48, 48, 45, { irregularity: 0.26, seed: 5 });
var lx, lz;
for (lx = 0; lx < 96; lx++) {
    for (lz = 0; lz < 96; lz++) {
        if (land(lx, lz)) fill(lx, 0, lz, lx, mesaY - 1, lz, strata);
    }
}
var h = hills({ base: mesaY, amp: 5, scale: 0.032, seed: 11, octaves: 2 });
var pad = flatten(h, { x0: 10, z0: 14, x1: 86, z1: 82 }, mesaY, 9);
surface(land, pad, patches([
    { block: 'coarse_dirt', w: 34 },
    { block: 'terracotta', w: 24 },
    { block: 'red_sand', w: 18 },
    { block: 'grass_block', w: 14 },
    { block: 'granite', w: 10 }
], 0.045, 17), { sub: 'coarse_dirt', deep: 'granite', depth: 3 });

// ===== 2. TRENCH + L-SHAPED ROCK-CUT DESCENT =====
var CX1 = 18, CX2 = 78, CZ1 = 22, CZ2 = 74;
fill(CX1, courtY + 1, CZ1, CX2, 28, CZ2, 'air');
fill(CX1, courtY, CZ1, CX2, courtY, CZ2, noisePalette([
    { block: 'smooth_red_sandstone', weight: 35 },
    { block: 'red_sandstone', weight: 25 },
    { block: 'polished_andesite', weight: 20 },
    { block: 'stone', weight: 12 },
    { block: 'gravel', weight: 8 }
], { seed: 44, freq: 0.1 }));

// flight A corridor: walk = 3 + (15 - x), clamped, over x 2..18 / z 44..52
var ax, aw;
for (ax = 2; ax <= 18; ax++) {
    aw = ax >= 15 ? courtWalk : courtWalk + (15 - ax);
    if (aw > 10) aw = 10;
    fill(ax, aw, 44, ax, 28, 52, 'air');
}
// flight B corridor: walk = 10 + (44 - z), over z 36..43 / x 2..9
var bz, bw;
for (bz = 36; bz <= 43; bz++) {
    bw = 10 + (44 - bz);
    if (bw > mesaY + 1) bw = mesaY + 1;
    fill(2, bw, bz, 9, 28, bz, 'air');
}
fill(2, mesaY + 1, 28, 9, 28, 36, 'air');   // canyon mouth on the mesa

staircase(15, 48, courtWalk, 10, 'west', { width: 3, material: 'polished_andesite_stairs', support: 'polished_andesite' });
landing(3, 10, 45, 6, 7, 'smooth_red_sandstone');
staircase(5, 44, 10, mesaY + 1, 'north', { width: 3, material: 'polished_andesite_stairs', support: 'polished_andesite' });

// rock protrusions on the cut faces (Cliffs doc: stairs/buttons as detail)
var pz, pxx;
for (pz = 26; pz <= 70; pz += 5) {
    ps(CX1, courtWalk + 3, pz, 'stone_button[face=wall,facing=east]');
    ps(CX2, courtWalk + 5, pz + 2, 'stone_button[face=wall,facing=west]');
    pb(CX1, courtWalk + 7, pz + 1, 'glow_lichen');
}
for (pxx = 24; pxx <= 72; pxx += 6) {
    ps(pxx, courtWalk + 4, CZ1, 'stone_button[face=wall,facing=south]');
    ps(pxx + 2, courtWalk + 6, CZ2, 'stone_button[face=wall,facing=north]');
}

// ===== 3. THE MONOLITH — THREE BATTERED TIERS, DIRECT PALETTES =====
var bodyPal = noisePalette([
    { block: 'red_sandstone', weight: 30 },
    { block: 'cut_red_sandstone', weight: 20 },
    { block: 'smooth_red_sandstone', weight: 16 },
    { block: 'terracotta', weight: 14 },
    { block: 'brown_terracotta', weight: 12 },
    { block: 'granite', weight: 8 }
], { seed: 7, freq: 0.1 });
var darkBase = noisePalette([
    { block: 'polished_basalt', weight: 34 },
    { block: 'brown_terracotta', weight: 26 },
    { block: 'basalt', weight: 22 },
    { block: 'deepslate_bricks', weight: 18 }
], { seed: 71, freq: 0.11 });

var tx, tz;
for (tx = NX1 - 2; tx <= NX2 + 2; tx++) {
    for (tz = NZ1 - 2; tz <= NZ2 + 2; tz++) {
        if (cross(tx, tz, 1)) fill(tx, t1, tz, tx, t1 + 1, tz, darkBase);   // stylobate, 1 proud (dark)
        if (cross(tx, tz, 0)) fill(tx, t1 + 2, tz, tx, t2 - 1, tz, bodyPal);
        if (cross(tx, tz, -1)) fill(tx, t2, tz, tx, t3 - 1, tz, bodyPal);
        if (cross(tx, tz, -2)) fill(tx, t3, tz, tx, ceilY, tz, bodyPal);
        if (cross(tx, tz, -1)) fill(tx, roofY, tz, tx, roofY, tz, noisePalette([
            { block: 'smooth_red_sandstone', weight: 50 },
            { block: 'cut_red_sandstone', weight: 30 },
            { block: 'red_sandstone', weight: 20 }
        ], { seed: 8, freq: 0.13 }));
    }
}

// carved bands + CAVETTO cornice at every tier top (Egyptian)
function bandAndCornice(g, yBand) {
    var bx, bz2, d;
    var dx = [1, -1, 0, 0], dz = [0, 0, 1, -1], nm = ['east', 'west', 'south', 'north'];
    for (bx = NX1 - 3; bx <= NX2 + 3; bx++) {
        for (bz2 = NZ1 - 3; bz2 <= NZ2 + 3; bz2++) {
            if (!cross(bx, bz2, g)) continue;
            for (d = 0; d < 4; d++) {
                if (cross(bx + dx[d], bz2 + dz[d], g)) continue;
                pb(bx, yBand, bz2, 'polished_basalt');
                roofStair(bx + dx[d], yBand, bz2 + dz[d], 'red_sandstone_stairs', nm[d], 'top');
            }
        }
    }
}
bandAndCornice(0, t2 - 1);
bandAndCornice(-1, t3 - 1);
bandAndCornice(-2, ceilY);

// pilaster relief: proud strips every 4 blocks up each face
var qx, qz, d2;
var ddx = [1, -1, 0, 0], ddz = [0, 0, 1, -1];
for (qx = NX1 - 2; qx <= NX2 + 2; qx++) {
    for (qz = NZ1 - 2; qz <= NZ2 + 2; qz++) {
        if (!cross(qx, qz, 0)) continue;
        if ((qx + qz) % 4 !== 0) continue;
        for (d2 = 0; d2 < 4; d2++) {
            if (cross(qx + ddx[d2], qz + ddz[d2], 0)) continue;
            fill(qx + ddx[d2], t1 + 2, qz + ddz[d2], qx + ddx[d2], t2 - 2, qz + ddz[d2], noisePalette([{ block: 'brown_terracotta', weight: 45 }, { block: 'granite', weight: 30 }, { block: 'cut_red_sandstone', weight: 25 }], { seed: 77, freq: 0.14 }));
        }
    }
}

// ===== 4. HOLLOW THE HYPOSTYLE HALL =====
fill(INX1, walkY, INZ1, INX2, ceilY - 1, INZ2, 'air');
fill(ITX1, walkY, ITZ1, ITX2, ceilY - 1, ITZ2, 'air');
fill(INX1, floorY, INZ1, INX2, floorY, INZ2, noisePalette([
    { block: 'smooth_red_sandstone', weight: 45 },
    { block: 'cut_red_sandstone', weight: 30 },
    { block: 'polished_andesite', weight: 25 }
], { seed: 61, freq: 0.12 }));
fill(ITX1, floorY, ITZ1, ITX2, floorY, ITZ2, noisePalette([
    { block: 'smooth_red_sandstone', weight: 45 },
    { block: 'cut_red_sandstone', weight: 30 },
    { block: 'polished_andesite', weight: 25 }
], { seed: 61, freq: 0.12 }));

// hypostyle: 2x2 columns, spacing 4
var colX = [44, 50], colZ = [36, 41, 53, 58, 62];
var ci, cj;
for (ci = 0; ci < colX.length; ci++) {
    for (cj = 0; cj < colZ.length; cj++) {
        fill(colX[ci], walkY, colZ[cj], colX[ci] + 1, ceilY - 2, colZ[cj] + 1, 'cut_red_sandstone');
        fill(colX[ci], ceilY - 1, colZ[cj], colX[ci] + 1, ceilY - 1, colZ[cj] + 1, 'chiseled_red_sandstone');
        fill(colX[ci], walkY, colZ[cj], colX[ci] + 1, walkY, colZ[cj] + 1, 'smooth_red_sandstone');
    }
}
// transept columns
var tcx = [34, 60], tk;
for (tk = 0; tk < 2; tk++) {
    fill(tcx[tk], walkY, 45, tcx[tk] + 1, ceilY - 2, 46, 'cut_red_sandstone');
    fill(tcx[tk], ceilY - 1, 45, tcx[tk] + 1, ceilY - 1, 46, 'chiseled_red_sandstone');
}

// ===== 5. WINDOWS — three registers, cut straight through =====
function cutWin(x1, y1, z1, x2, y2, z2) { fill(x1, y1, z1, x2, y2, z2, 'air'); }
// register 1 (Y7-9): tall ogee, 2 wide -> east & west nave walls
var wz;
for (wz = 36; wz <= 62; wz += 6) {
    cutWin(NX1, 7, wz, NX1 + 4, 9, wz + 1);
    cutWin(NX2 - 4, 7, wz, NX2, 9, wz + 1);
    pb(NX1 + 1, 7, wz, 'orange_stained_glass'); pb(NX1 + 1, 7, wz + 1, 'orange_stained_glass');
    pb(NX2 - 1, 7, wz, 'orange_stained_glass'); pb(NX2 - 1, 7, wz + 1, 'orange_stained_glass');
    // ogee head
    cutWin(NX1, 10, wz, NX1 + 4, 10, wz); cutWin(NX2 - 4, 10, wz + 1, NX2, 10, wz + 1);
}
// register 2 (Y11-12): keyhole on all four gable faces
var kx;
for (kx = 42; kx <= 53; kx += 5) {
    cutWin(kx, 11, NZ1, kx, 12, NZ1 + 4);
    cutWin(kx - 1, 12, NZ1, kx + 1, 12, NZ1 + 4);
    cutWin(kx, 11, NZ2 - 4, kx, 12, NZ2);
    cutWin(kx - 1, 12, NZ2 - 4, kx + 1, 12, NZ2);
}
var kz;
for (kz = 44; kz <= 49; kz += 5) {
    cutWin(TX1, 11, kz, TX1 + 4, 12, kz);
    cutWin(TX2 - 4, 11, kz, TX2, 12, kz);
}
// register 3 (Y14-15): small square lights, top tier
var sx2;
for (sx2 = 42; sx2 <= 53; sx2 += 4) {
    cutWin(sx2, 14, NZ1 + 2, sx2, 14, NZ1 + 6);
    cutWin(sx2, 14, NZ2 - 6, sx2, 14, NZ2 - 2);
}
var sz2;
for (sz2 = 38; sz2 <= 60; sz2 += 5) {
    cutWin(NX1 + 2, 14, sz2, NX1 + 6, 14, sz2);
    cutWin(NX2 - 6, 14, sz2, NX2 - 2, 14, sz2);
}

// ===== 6. DOOR, PORCH, FLIGHT INTO THE COURTYARD =====
fill(47, walkY, NZ2 - 4, 48, walkY + 2, NZ2 + 2, 'air');
fill(44, t1, NZ2 + 1, 51, floorY, NZ2 + 3, bodyPal);       // porch platform, top = floorY
fill(44, t1, NZ2 + 4, 51, t1 + 1, NZ2 + 4, bodyPal);
fill(44, t1, NZ2 + 5, 51, t1, NZ2 + 5, bodyPal);
staircase(47, NZ2 + 6, courtWalk, walkY, 'north', { width: 4, material: 'red_sandstone_stairs', support: 'red_sandstone' });
ps(46, walkY + 1, NZ2 + 1, 'red_sandstone_wall');
ps(49, walkY + 1, NZ2 + 1, 'red_sandstone_wall');
pb(46, walkY + 2, NZ2 + 1, 'lantern');
pb(49, walkY + 2, NZ2 + 1, 'lantern');

// ===== 7. INTERIOR: ALTAR, CARPETS, CANDLES =====
fill(45, walkY, 35, 50, walkY, 36, 'polished_andesite');           // sanctuary dais (north)
fill(46, walkY + 1, 35, 49, walkY + 1, 35, 'chiseled_red_sandstone');
ps(46, walkY + 2, 35, 'candle[candles=4,lit=true]');
ps(49, walkY + 2, 35, 'candle[candles=4,lit=true]');
pb(47, walkY + 2, 35, 'gold_block');
pb(48, walkY + 2, 35, 'gold_block');
ps(47, walkY + 1, 37, 'lectern[facing=south]');
ps(44, walkY + 2, 34, 'yellow_wall_banner[facing=south]');
ps(51, walkY + 2, 34, 'red_wall_banner[facing=south]');
rug(46, walkY, 38, 3, 26, 'red', 'orange');
rug(36, walkY, 46, 24, 3, 'orange', 'red');
rug(43, walkY, 34, 4, 3, 'yellow', 'red');
// candle stands between the columns
var cs2;
for (cs2 = 0; cs2 < colZ.length; cs2++) {
    pb(43, walkY, colZ[cs2], 'red_sandstone_wall');
    ps(43, walkY + 1, colZ[cs2], 'candle[candles=3,lit=true]');
    pb(52, walkY, colZ[cs2] + 1, 'red_sandstone_wall');
    ps(52, walkY + 1, colZ[cs2] + 1, 'candle[candles=3,lit=true]');
}
// chests, pots, prayer benches
var bnz;
for (bnz = 40; bnz <= 62; bnz += 6) {
    chairStair(45, walkY, bnz, 'dark_oak_stairs', 'north');
    chairStair(46, walkY, bnz, 'dark_oak_stairs', 'north');
    chairStair(49, walkY, bnz, 'dark_oak_stairs', 'north');
    chairStair(50, walkY, bnz, 'dark_oak_stairs', 'north');
}
pb(33, walkY, 45, 'decorated_pot'); pb(62, walkY, 48, 'decorated_pot');
pb(33, walkY, 48, 'barrel'); pb(62, walkY, 45, 'chest[facing=west]');
// ceiling lanterns
var hl = [[44, 44], [51, 44], [44, 50], [51, 50], [47, 40], [47, 60], [36, 47], [59, 47]];
var hi;
for (hi = 0; hi < hl.length; hi++) {
    ps(hl[hi][0], ceilY - 1, hl[hi][1], 'chain[axis=y]');
    ps(hl[hi][0], ceilY - 2, hl[hi][1], 'lantern[hanging=true]');
}

// ===== 8. ROOF CROSS MOTIF (raised relief) =====
var ma, mb;
for (ma = -12; ma <= 12; ma++) {
    for (mb = -3; mb <= 3; mb++) {
        pb(48 + ma, motifY, 48 + mb, 'chiseled_red_sandstone');
        pb(48 + mb, motifY, 48 + ma, 'chiseled_red_sandstone');
    }
}
for (ma = -8; ma <= 8; ma++) {
    for (mb = -1; mb <= 1; mb++) {
        pb(48 + ma, motifY + 1, 48 + mb, 'cut_red_sandstone');
        pb(48 + mb, motifY + 1, 48 + ma, 'cut_red_sandstone');
    }
}

// ===== 9. MESA SURFACE: PILGRIM PATH, LAMPS, SCRUB =====
var gi;
for (gi = 26; gi <= 70; gi += 9) {
    lampPost(CX1 - 3, mesaY + 1, gi, 5);
    lampPost(CX2 + 3, mesaY + 1, gi, 5);
}
var ppz, ppx;
for (ppz = 20; ppz <= 34; ppz++) {
    for (ppx = 3; ppx <= 9; ppx++) {
        pb(ppx, Math.round(pad(ppx, ppz)), ppz, 'dirt_path');
    }
}
var trs = [[16, 14], [12, 70], [22, 84], [58, 86], [84, 40], [80, 74], [40, 12], [70, 16]];
var ti2, ty2;
for (ti2 = 0; ti2 < trs.length; ti2++) {
    if (!land(trs[ti2][0], trs[ti2][1])) continue;
    ty2 = Math.round(pad(trs[ti2][0], trs[ti2][1])) + 1;
    try { placeTree(trs[ti2][0], ty2, trs[ti2][1], 'acacia'); } catch (err) { }
}
var vx, vz, vy;
for (vx = 2; vx < 94; vx += 3) {
    for (vz = 2; vz < 94; vz += 3) {
        if (vx > 14 && vx < 82 && vz > 18 && vz < 78) continue;
        if (!land(vx, vz)) continue;
        if (noise2d('perlin', vx, vz, { freq: 0.15, seed: 23 }) > 0.3) {
            vy = Math.round(pad(vx, vz)) + 1;
            pb(vx, vy, vz, (vx + vz) % 4 === 0 ? 'dead_bush' : 'short_grass');
        }
    }
}
// rubble in the courtyard
fill(22, courtWalk, 26, 25, courtWalk + 1, 29, 'cobblestone');
fill(72, courtWalk, 68, 75, courtWalk + 1, 71, 'mossy_cobblestone');

Static Renders

Rock-hewn monolithic church (Lalibela) — indexed isometric viewisometric
Rock-hewn monolithic church (Lalibela) — indexed front viewfront
Rock-hewn monolithic church (Lalibela) — indexed side viewside
Rock-hewn monolithic church (Lalibela) — indexed back viewback
Rock-hewn monolithic church (Lalibela) — indexed top viewtop