← Back to Gallery

Loading 3D viewer…

Rock-hewn monolithic church (Lalibela) — guided

by Jorge Barbero

Dimensions
96×40×96
Blocks
174,867
Compute Time
44.1s
Generation Time
12m 18s
Edits
3
Model
claude-opus-5
Created
August 29, 2026
autoevalguidedarchitecturerock-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=guided. prepare_build + docs: build-rules/Essential Build Rules, interior-design/Room-by-Room, worldedit-api/Natural Terrain Superpowers, worldedit-api/Position Palettes, worldedit-api/Furniture & Stairs, worldedit-api/Tree Generation. LAYERED process with placeholder wool + masks, replaceMasked texturing, staircase() for both descents, terrain superpowers for the plateau.

Photos

Monolito en la trinchera
Monolito en la trincheraexterior
Meseta y trinchera
Meseta y trincheraexterior
Nave y altar
Nave y altarinterior
Escalera excavada
Escalera excavadaexterior
CraftScript Source
// === BUILD PLAN ===
// Style: Ethiopian rock-hewn monolithic church (Lalibela / Bete Giyorgis)
// Footprint: GREEK CROSS monolith, arms 16 wide, bounding 30x30, standing free inside a
//            57x57 rectangular TRENCH cut 13 blocks down into a red tuff bedrock plateau.
// Height: trench floor Y2 -> plinth Y3-4 -> body Y5-14 -> roof tiers Y15,Y16. Plateau top Y15.
//
// SITE (top-down, 1 char = 4 blocks):
//   ................
//   ..############..      # = bedrock plateau (organic relief via hills+flatten+surface)
//   ..#..______..#..      _ = trench courtyard (rock floor Y2)
//   ..#.._+##+_..#..      + = monolith cross arms
//   ..#.._####_..#..
//   ..#.._+##+_..#..
//   ..#..___||__.#..      || = rock-cut slot canyon + descending staircase (Y16 -> Y3)
//   ..############..
//
// CROSS PLAN (interior, walls 3 thick):
//   outer A x33..62 z40..55   |  outer B x40..55 z33..62
//   inner A x36..59 z43..52   |  inner B x43..52 z36..59
//   nave floor Y5, walkY 6, ceiling Y14 (8 blocks of air), 8 monolithic pillars
//
// FRONT ELEVATION (south arm, +Z = camera side):
//     [====== roof tier 2 + carved cross =====]
//     [======== roof tier 1 + cornice ========]
//     | reg3  o    o    o   |   small ogees Y12
//     | reg2  ^    ^    ^   |   keyhole windows Y10
//     | reg1  +    +    +   |   latin-cross windows Y7 (orange glass)
//     |  pilaster relief    |
//     ===== DOOR + porch + staircase down to courtyard =====
//
// MATERIALS PLAN:
//   bedrock  : noisePalette granite/terracotta/red_sandstone/brown_terracotta  - WHY: one rock mass, organic blotches, reads carved
//   monolith : gradientPalette Y3->Y14 brown_terracotta -> terracotta -> red_terracotta -> granite {noise .3}
//              - WHY: the monolith is the SAME rock, but sun-bleached higher up; the ramp fakes shadow at the base
//   roof     : noisePalette polished_granite/terracotta/red_terracotta + chiseled cross motif - WHY: crisper = "carved"
//   cornice  : polished_granite_stairs upside-down (roofStair top) - WHY: 1-block shadow line = wall_depth
//   interior : polished_granite floor + red/orange carpet runner, chiseled_stone_bricks pillars, lanterns+candles
//   process  : LAYERED (blue_wool body / red_wool roof placeholders -> masks -> replaceMasked palette)
// Rooms: nave (pillars, benches, runner, lanterns), sanctuary (altar, candles, banners), side aisles (rugs, pots)
// STAIR PLAN: descent  D=13 -> staircase(47,92, walk16 -> walk3, 'north') width 3 in a slot canyon
//             porch    D=3  -> staircase(47,68, walk3 -> walk6, 'north') width 2 onto the plinth porch
// === END PLAN ===

// ---------- Y LEVELS ----------
var trenchFloorY = 2;      // top block of the courtyard
var courtWalkY = 3;        // player feet in the trench
var plinthY = 3;
var floorY = 5;            // nave floor block
var walkY = 6;             // furniture / doors
var ceilY = 14;            // ceiling block layer
var roof1Y = 15;
var roof2Y = 16;
var groundY = 15;          // plateau top block  (walk 16)

// ---------- CROSS GEOMETRY ----------
var AX1 = 33, AX2 = 62, AZ1 = 40, AZ2 = 55;   // E-W arm
var BX1 = 40, BX2 = 55, BZ1 = 33, BZ2 = 62;   // N-S arm
var IAX1 = 36, IAX2 = 59, IAZ1 = 43, IAZ2 = 52;
var IBX1 = 43, IBX2 = 52, IBZ1 = 36, IBZ2 = 59;

function inCross(x, z) {
    if (x >= AX1 && x <= AX2 && z >= AZ1 && z <= AZ2) return true;
    if (x >= BX1 && x <= BX2 && z >= BZ1 && z <= BZ2) return true;
    return false;
}
function inCrossGrow(x, z, g) {
    if (x >= AX1 - g && x <= AX2 + g && z >= AZ1 - g && z <= AZ2 + g) return true;
    if (x >= BX1 - g && x <= BX2 + g && z >= BZ1 - g && z <= BZ2 + g) return true;
    return false;
}
function inNave(x, z) {
    if (x >= IAX1 && x <= IAX2 && z >= IAZ1 && z <= IAZ2) return true;
    if (x >= IBX1 && x <= IBX2 && z >= IBZ1 && z <= IBZ2) return true;
    return false;
}

// ===================== 1. BEDROCK PLATEAU (terrain superpowers) =====================
var rockPal = noisePalette([
    { block: 'granite', weight: 30 },
    { block: 'terracotta', weight: 25 },
    { block: 'red_sandstone', weight: 20 },
    { block: 'brown_terracotta', weight: 15 },
    { block: 'stone', weight: 10 }
], { seed: 4, freq: 0.07 });

var mesa = blob(48, 48, 46, { irregularity: 0.22, seed: 5 });
var mx, mz;
for (mx = 0; mx < 96; mx++) {
    for (mz = 0; mz < 96; mz++) {
        if (mesa(mx, mz)) fill(mx, 0, mz, mx, groundY - 1, mz, rockPal);
    }
}

var h = hills({ base: groundY, amp: 6, scale: 0.03, seed: 3, octaves: 2 });
var pad = flatten(h, { x0: 14, z0: 14, x1: 82, z1: 82 }, groundY, 8);
var topMat = patches([
    { block: 'coarse_dirt', w: 30 },
    { block: 'terracotta', w: 25 },
    { block: 'granite', w: 20 },
    { block: 'grass_block', w: 15 },
    { block: 'red_sand', w: 10 }
], 0.05, 9);
surface(mesa, pad, topMat, { sub: 'coarse_dirt', deep: 'granite', depth: 3 });

// ===================== 2. TRENCH + SLOT CANYON =====================
var TX1 = 20, TX2 = 76, TZ1 = 20, TZ2 = 76;
fill(TX1, trenchFloorY + 1, TZ1, TX2, 26, TZ2, 'air');
fill(TX1, trenchFloorY, TZ1, TX2, trenchFloorY, TZ2, noisePalette([
    { block: 'polished_andesite', weight: 35 },
    { block: 'andesite', weight: 25 },
    { block: 'stone', weight: 20 },
    { block: 'granite', weight: 12 },
    { block: 'gravel', weight: 8 }
], { seed: 12, freq: 0.11 }));

// slot canyon: air follows the descending walk line (walk = 3 at z79 -> 16 at z92)
var zz, wlk;
for (zz = 77; zz <= 92; zz++) {
    wlk = zz <= 79 ? courtWalkY : courtWalkY + (zz - 79);
    fill(44, wlk, zz, 51, 26, zz, 'air');
}
// mouth of the canyon on the plateau
fill(44, groundY + 1, 93, 51, 26, 95, 'air');

// carved votive niches in the trench walls (rock-cut tombs)
var nz;
for (nz = 28; nz <= 68; nz += 10) {
    fill(TX1 - 3, courtWalkY + 1, nz, TX1 - 1, courtWalkY + 3, nz + 1, 'air');
    fill(TX2 + 1, courtWalkY + 1, nz, TX2 + 3, courtWalkY + 3, nz + 1, 'air');
}
var nx;
for (nx = 28; nx <= 68; nx += 10) {
    fill(nx, courtWalkY + 1, TZ1 - 3, nx + 1, courtWalkY + 3, TZ1 - 1, 'air');
}

// ===================== 3. MONOLITH — FORM IN PLACEHOLDERS (LAYERED) =====================
var PH = 'blue_wool';
var PR = 'red_wool';
var gx, gz;

// plinth (2 stepped tiers, wider than the body) + body
for (gx = AX1 - 2; gx <= AX2 + 2; gx++) {
    for (gz = BZ1 - 2; gz <= BZ2 + 2; gz++) {
        if (inCrossGrow(gx, gz, 2)) fill(gx, plinthY, gz, gx, plinthY, gz, PH);
        if (inCrossGrow(gx, gz, 1)) fill(gx, plinthY + 1, gz, gx, plinthY + 1, gz, PH);
        if (inCross(gx, gz)) fill(gx, floorY, gz, gx, ceilY, gz, PH);
    }
}
// roof tiers
for (gx = AX1 - 1; gx <= AX2 + 1; gx++) {
    for (gz = BZ1 - 1; gz <= BZ2 + 1; gz++) {
        if (inCrossGrow(gx, gz, 1)) fill(gx, roof1Y, gz, gx, roof1Y, gz, PR);
        if (inCrossGrow(gx, gz, -3)) fill(gx, roof2Y, gz, gx, roof2Y, gz, PR);
    }
}

// pilaster relief: one proud column on every 5th perimeter cell, pushed OUTWARD
var dirsX = [1, -1, 0, 0];
var dirsZ = [0, 0, 1, -1];
var edgeName = ['east', 'west', 'south', 'north'];
var di;
for (gx = AX1 - 1; gx <= AX2 + 1; gx++) {
    for (gz = BZ1 - 1; gz <= BZ2 + 1; gz++) {
        if (!inCross(gx, gz)) continue;
        for (di = 0; di < 4; di++) {
            if (inCross(gx + dirsX[di], gz + dirsZ[di])) continue;
            // cornice course all the way round (upside-down stairs = shadow line)
            roofStair(gx + dirsX[di], ceilY, gz + dirsZ[di], 'polished_granite_stairs', edgeName[di], 'top');
            if ((gx + gz) % 5 === 0) {
                fill(gx + dirsX[di], floorY, gz + dirsZ[di], gx + dirsX[di], ceilY - 1, gz + dirsZ[di], PH);
            }
        }
    }
}

// ===================== 4. CARVE THE NAVE (masks: body only, roof untouched) =====================
clearMasked(IAX1, walkY, IAZ1, IAX2, ceilY - 1, IAZ2, onlyMaterial(PH));
clearMasked(IBX1, walkY, IBZ1, IBX2, ceilY - 1, IBZ2, onlyMaterial(PH));

// monolithic pillars back into the nave
var pillars = [[38, 44], [38, 51], [57, 44], [57, 51], [44, 38], [51, 38], [44, 57], [51, 57]];
var pi;
for (pi = 0; pi < pillars.length; pi++) {
    fill(pillars[pi][0], walkY, pillars[pi][1], pillars[pi][0], ceilY - 1, pillars[pi][1], PH);
}

// ===================== 5. WINDOWS — 3 REGISTERS, CARVED WITH MASKS =====================
// shape = list of [du, dy] cells on the face; carved through the full 3-block wall thickness
var CROSSWIN = [[0, 0], [0, 1], [0, 2], [0, 3], [-1, 2], [1, 2]];
var KEYWIN = [[0, 0], [0, 1], [-1, 2], [0, 2], [1, 2], [0, 3]];
var OGEEWIN = [[0, 0], [0, 1], [0, 2]];

function carveWin(ax, az, y, shape, axis, glaze) {
    // axis 'x' -> face normal is +/-X, opening runs along Z ; axis 'z' -> runs along X
    var k, du, dy, cx, cz, t;
    for (k = 0; k < shape.length; k++) {
        du = shape[k][0];
        dy = shape[k][1];
        for (t = -3; t <= 3; t++) {
            if (axis === 'x') { cx = ax + t; cz = az + du; }
            else { cx = ax + du; cz = az + t; }
            clearMasked(cx, y + dy, cz, cx, y + dy, cz, onlyMaterial(PH));
        }
    }
    if (glaze) {
        for (k = 0; k < shape.length; k++) {
            du = shape[k][0];
            dy = shape[k][1];
            if (axis === 'x') { cx = ax; cz = az + du; } else { cx = ax + du; cz = az; }
            pb(cx, y + dy, cz, 'orange_stained_glass');
        }
    }
}

// arm-end facades (4) — three registers each
var ends = [[AX2 - 1, 47, 'x'], [AX1 + 1, 47, 'x'], [47, BZ1 + 1, 'z'], [47, BZ2 - 1, 'z']];
var ei, e;
for (ei = 0; ei < ends.length; ei++) {
    e = ends[ei];
    if (e[2] === 'x') {
        carveWin(e[0], 44, 7, CROSSWIN, 'x', true);
        carveWin(e[0], 51, 7, CROSSWIN, 'x', true);
        carveWin(e[0], 47, 10, KEYWIN, 'x', false);
        carveWin(e[0], 44, 12, OGEEWIN, 'x', false);
        carveWin(e[0], 51, 12, OGEEWIN, 'x', false);
    } else {
        carveWin(44, e[1], 7, CROSSWIN, 'z', true);
        carveWin(51, e[1], 7, CROSSWIN, 'z', true);
        carveWin(47, e[1], 10, KEYWIN, 'z', false);
        carveWin(44, e[1], 12, OGEEWIN, 'z', false);
        carveWin(51, e[1], 12, OGEEWIN, 'z', false);
    }
}
// long side walls of both arms
var sx;
for (sx = 37; sx <= 58; sx += 6) {
    if (sx > 41 && sx < 54) continue;
    carveWin(sx, AZ1 + 1, 7, CROSSWIN, 'z', true);
    carveWin(sx, AZ2 - 1, 7, CROSSWIN, 'z', true);
    carveWin(sx, AZ1 + 1, 11, OGEEWIN, 'z', false);
    carveWin(sx, AZ2 - 1, 11, OGEEWIN, 'z', false);
}
var sz;
for (sz = 37; sz <= 58; sz += 6) {
    if (sz > 41 && sz < 54) continue;
    carveWin(BX1 + 1, sz, 7, CROSSWIN, 'x', true);
    carveWin(BX2 - 1, sz, 7, CROSSWIN, 'x', true);
    carveWin(BX1 + 1, sz, 11, OGEEWIN, 'x', false);
    carveWin(BX2 - 1, sz, 11, OGEEWIN, 'x', false);
}

// ===================== 6. DOOR + PORCH + STAIRS =====================
clearMasked(47, walkY, BZ2 - 2, 48, walkY + 2, BZ2 + 1, onlyMaterial(PH));
fill(45, plinthY, 63, 50, floorY, 65, PH);      // porch platform, top = floorY (walk 6)
fill(45, plinthY, 66, 50, plinthY + 1, 66, PH); // support under the rising steps
fill(45, plinthY, 67, 50, plinthY, 67, PH);
staircase(47, 68, courtWalkY, walkY, 'north', { width: 2, material: 'polished_granite_stairs', support: 'polished_granite' });

// the big rock-cut descent into the trench
staircase(47, 79, courtWalkY, groundY + 1, 'south', { width: 3, material: 'polished_andesite_stairs', support: 'polished_andesite' });

// ===================== 7. TEXTURE LAST (replaceMasked) =====================
var bodyPal = gradientPalette(plinthY, ceilY, [
    { block: 'brown_terracotta', from: 0 },
    { block: 'terracotta', from: 0.3 },
    { block: 'red_terracotta', from: 0.62 },
    { block: 'granite', from: 0.86 }
], { noise: 0.3 });
replaceMasked(AX1 - 4, 0, BZ1 - 4, AX2 + 4, roof2Y + 1, BZ2 + 4, bodyPal, onlyMaterial(PH));

var roofPal = noisePalette([
    { block: 'polished_granite', weight: 45 },
    { block: 'terracotta', weight: 30 },
    { block: 'red_terracotta', weight: 25 }
], { seed: 21, freq: 0.14 });
replaceMasked(AX1 - 4, roof1Y, BZ1 - 4, AX2 + 4, roof2Y, BZ2 + 4, roofPal, onlyMaterial(PR));

// carved nested cross motif on the roof (Bete Giyorgis signature)
function crossMotif(cx, cz, arm, wdt, mat) {
    var a, bq;
    for (a = -arm; a <= arm; a++) {
        for (bq = -wdt; bq <= wdt; bq++) {
            pb(cx + a, roof2Y, cz + bq, mat);
            pb(cx + bq, roof2Y, cz + a, mat);
        }
    }
}
crossMotif(47, 47, 11, 3, 'polished_diorite');
crossMotif(47, 47, 8, 2, 'chiseled_stone_bricks');
crossMotif(47, 47, 5, 1, 'polished_andesite');

// ===================== 8. NAVE FLOOR, PILLARS, FURNITURE =====================
var floorPal = noisePalette([
    { block: 'polished_andesite', weight: 40 },
    { block: 'polished_granite', weight: 30 },
    { block: 'smooth_stone', weight: 20 },
    { block: 'polished_diorite', weight: 10 }
], { seed: 33, freq: 0.12 });
for (gx = IAX1; gx <= IAX2; gx++) {
    for (gz = IBZ1; gz <= IBZ2; gz++) {
        if (inNave(gx, gz)) pb(gx, floorY, gz, floorPal(gx, floorY, gz));
    }
}
// pillars: base, shaft, capital
for (pi = 0; pi < pillars.length; pi++) {
    var px = pillars[pi][0], pz = pillars[pi][1];
    pb(px, walkY, pz, 'chiseled_stone_bricks');
    fill(px, walkY + 1, pz, px, ceilY - 2, pz, 'polished_granite');
    pb(px, ceilY - 1, pz, 'chiseled_stone_bricks');
    ps(px, ceilY - 2, pz + 1, 'stone_brick_stairs[facing=south,half=top]');
    ps(px, ceilY - 2, pz - 1, 'stone_brick_stairs[facing=north,half=top]');
    ps(px + 1, ceilY - 2, pz, 'stone_brick_stairs[facing=east,half=top]');
    ps(px - 1, ceilY - 2, pz, 'stone_brick_stairs[facing=west,half=top]');
}
// carpet runners: nave (N-S) + transept (E-W)
rug(46, walkY, 39, 3, 21, 'red', 'yellow');
rug(38, walkY, 46, 20, 3, 'red', 'yellow');

// SANCTUARY (north arm, z 37..40): raised stone altar + candles + banners
fill(44, walkY, 37, 51, walkY, 38, 'polished_andesite');
fill(45, walkY + 1, 37, 50, walkY + 1, 37, 'chiseled_stone_bricks');
ps(46, walkY + 2, 37, 'candle[candles=3,lit=true]');
ps(49, walkY + 2, 37, 'candle[candles=3,lit=true]');
pb(47, walkY + 2, 37, 'gold_block');
pb(48, walkY + 2, 37, 'gold_block');
ps(45, walkY + 2, 36, 'yellow_wall_banner[facing=south]');
ps(50, walkY + 2, 36, 'red_wall_banner[facing=south]');
ps(47, walkY + 1, 39, 'lectern[facing=south]');
pb(44, walkY + 1, 38, 'lantern');
pb(51, walkY + 1, 38, 'lantern');

// benches in the nave, facing the sanctuary
var bx, bz2;
for (bz2 = 44; bz2 <= 55; bz2 += 3) {
    for (bx = 44; bx <= 51; bx++) {
        if (bx === 47 || bx === 48) continue;
        chairStair(bx, walkY, bz2, 'dark_oak_stairs', 'north');
    }
}
// side aisles: pots, barrels, rugs, candle stands
var ai;
for (ai = 0; ai < 4; ai++) {
    pb(37 + ai * 7, walkY, 44, 'decorated_pot');
    pb(37 + ai * 7, walkY, 51, 'decorated_pot');
}
rug(37, walkY, 47, 4, 3, 'orange', 'brown');
rug(56, walkY, 47, 4, 3, 'orange', 'brown');
pb(37, walkY, 45, 'barrel');
pb(58, walkY, 50, 'barrel');
ps(58, walkY, 45, 'cauldron[level=0]');
// candle stands along the walls
var cs;
for (cs = 44; cs <= 51; cs += 7) {
    pb(37, walkY, cs, 'cobblestone_wall');
    ps(37, walkY + 1, cs, 'candle[candles=2,lit=true]');
    pb(58, walkY, cs, 'cobblestone_wall');
    ps(58, walkY + 1, cs, 'candle[candles=2,lit=true]');
}
// hanging lanterns from the ceiling
var lp = [[44, 44], [51, 44], [44, 51], [51, 51], [47, 41], [47, 57], [41, 47], [54, 47]];
var li;
for (li = 0; li < lp.length; li++) {
    ps(lp[li][0], ceilY - 1, lp[li][1], 'chain[axis=y]');
    ps(lp[li][0], ceilY - 2, lp[li][1], 'lantern[hanging=true]');
}

// ===================== 9. COURTYARD + PLATEAU DETAIL =====================
// torches on the trench rim + processional path to the canyon mouth
var ri;
for (ri = 24; ri <= 72; ri += 8) {
    lampPost(TX1 - 2, groundY + 1, ri, 4);
    lampPost(TX2 + 2, groundY + 1, ri, 4);
}
for (ri = 24; ri <= 72; ri += 8) {
    pb(ri, courtWalkY, TZ1 + 1, 'cobblestone_wall');
    ps(ri, courtWalkY + 1, TZ1 + 1, 'lantern');
}
// dirt path across the plateau to the stair mouth
var pz2, pw;
for (pz2 = 84; pz2 <= 95; pz2++) {
    for (pw = 44; pw <= 51; pw++) {
        pb(pw, Math.round(pad(pw, pz2)), pz2, 'dirt_path');
    }
}
// acacia grove + dry scrub on the plateau (outside the trench)
var trees = [[10, 12], [14, 60], [9, 84], [30, 88], [64, 90], [86, 20], [88, 58], [82, 86], [26, 8], [70, 10]];
var ti, ty;
for (ti = 0; ti < trees.length; ti++) {
    ty = Math.round(pad(trees[ti][0], trees[ti][1])) + 1;
    try { placeTree(trees[ti][0], ty, trees[ti][1], 'acacia'); } catch (err) { }
}
var scx, scz, sy;
for (scx = 4; scx < 94; scx += 3) {
    for (scz = 4; scz < 94; scz += 3) {
        if (scx > 14 && scx < 82 && scz > 14 && scz < 82) continue;
        if (noise2d('perlin', scx, scz, { freq: 0.16, seed: 6 }) > 0.35) {
            sy = Math.round(pad(scx, scz)) + 1;
            pb(scx, sy, scz, (scx + scz) % 3 === 0 ? 'dead_bush' : 'short_grass');
        }
    }
}
// boulders spilled in the courtyard corners
fill(24, courtWalkY, 24, 26, courtWalkY + 1, 26, 'mossy_cobblestone');
fill(71, courtWalkY, 71, 73, courtWalkY + 1, 73, 'mossy_cobblestone');

// final guarantee: nothing intrudes into the nave volume above the furniture line
clearMasked(IAX1, walkY + 3, IAZ1, IAX2, ceilY - 1, IAZ2, onlyMaterial('coarse_dirt'));


// ===== FIX: ningun placeholder puede sobrevivir en el volumen =====
replaceMasked(0, 0, 0, 95, 30, 95, bodyPal, onlyMaterial(PH));
replaceMasked(0, 0, 0, 95, 30, 95, roofPal, onlyMaterial(PR));

Static Renders

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