#include "world/manor.h" #include "world/terrain.h" #include #include /* Heights (m from the ground) */ #define FL 0.70f /* the ground floor: four steps up */ #define STEP_H (FL / 4.0f) #define CEIL 3.40f /* its ceiling ... */ #define SLAB 0.20f /* ... this thick */ #define WALL_TOP 6.20f /* the eaves (two storeys over the basement) */ #define PORCH_TOP 3.00f #define DOOR_H 2.15f /* The halves */ #define LEFT_Z0 -5.0f #define LEFT_Z1 5.0f #define RIGHT_Z0 -5.0f /* (the right half stands a little further out at the front: its back in line) */ #define RIGHT_Z1 5.8f #define HALF_LEN 18.0f #define T 0.30f /* outer walls */ #define P 0.15f /* inner walls */ #define PORCH_Z1 7.3f /* the porch: 1.5 m in front of the right half */ #define PORCH_X0 9.5f /* (the right half of the front) */ #define ROOF_PITCH (5.0f / 12.0f) #define RIGHT_ROOF 4.5f /* the right half's roof: the ridge across (front to back), this high over the eaves */ static const Vec3 PLASTER = { 0.88f, 0.82f, 0.68f }; static const Vec3 PLINTH = { 0.62f, 0.60f, 0.56f }; static const Vec3 INSIDE = { 0.93f, 0.91f, 0.85f }; static const Vec3 CEILING = { 0.95f, 0.94f, 0.90f }; static const Vec3 FLOORING = { 0.62f, 0.45f, 0.30f }; static const Vec3 TILES = { 0.70f, 0.66f, 0.60f }; static const Vec3 STEPS = { 0.58f, 0.56f, 0.53f }; static const Vec3 RAIL = { 0.45f, 0.32f, 0.22f }; static const Vec3 FRAME = { 0.93f, 0.91f, 0.86f }; static const Vec3 PORCELAIN = { 0.95f, 0.95f, 0.93f }; static const Vec3 PORCH_ROOF = { 0.38f, 0.38f, 0.43f }; enum { LOOK_ONLY, SOLID, EAVES_WALL }; #define MAX_SLABS 720 #define MAX_OPENINGS 96 typedef struct { ShapeSlab slabs[MAX_SLABS]; int n_slabs; ShapeOpening openings[MAX_OPENINGS]; int n_openings; ShapeRoof roofs[2]; int n_roofs; World *world; Vec3 origin; float yaw; bool ok; bool brick; /* boxes made now are brick (mortar lines) */ unsigned rng; } Plan; /* Which way a door, window or piece of furniture faces (local) */ static const Vec3 FACE_PX = { 1.0f, 0.0f, 0.0f }, FACE_NX = { -1.0f, 0.0f, 0.0f }; static const Vec3 FACE_PZ = { 0.0f, 0.0f, 1.0f }, FACE_NZ = { 0.0f, 0.0f, -1.0f }; static float rnd(Plan *p) { p->rng = p->rng * 1664525u + 1013904223u; return (float)(p->rng >> 8) / 16777216.0f; } static Vec3 toWorld(const Plan *p, Vec3 local) { return vec3_add(p->origin, mat4_mul_dir(mat4_rotate_yxz(vec3(0.0f, p->yaw, 0.0f)), local)); } /* A solid box of the building (it's walked on, leaned on, run into). */ static Object *solid(Plan *p, Vec3 min, Vec3 max, int kind, const char *name) { Vec3 size = vec3_sub(max, min); Vec3 foot = vec3((min.x + max.x) * 0.5f, min.y, (min.z + max.z) * 0.5f); Object *o = Object_create(OBJ_WALL, PALETTE_GROUND, toWorld(p, foot), size, 1000.0f); if (!o) { p->ok = false; return NULL; } Object_setRotation(o, vec3(0.0f, p->yaw, 0.0f)); o->variant = kind == EAVES_WALL ? 2 : 0; if (name) snprintf(o->name, sizeof(o->name), "%s", name); if (!World_add(p->world, o)) { p->ok = false; return NULL; } return o; } /* A box: seen (colour), and solid unless LOOK_ONLY. */ static void box(Plan *p, Vec3 min, Vec3 max, Vec3 color, int kind) { if (max.x - min.x <= 1e-3f || max.y - min.y <= 1e-3f || max.z - min.z <= 1e-3f) return; if (p->n_slabs < MAX_SLABS) p->slabs[p->n_slabs++] = (ShapeSlab){ min, max, color, p->brick }; else p->ok = false; if (kind != LOOK_ONLY) solid(p, min, max, kind, NULL); } /* A window, door or frame on a wall face at at, its front facing face (local). */ static void opening(Plan *p, OpeningKind kind, Vec3 at, Vec3 face, float w, float h) { if (p->n_openings < MAX_OPENINGS) p->openings[p->n_openings++] = (ShapeOpening){ kind, at, yaw_facing(face), w, h, FRAME }; else p->ok = false; } /* A gap in a wall: from a to b along it, from y0 to y1 */ typedef struct { float a, b, y0, y1; } Gap; /* A wall along x (from x0 to x1, between z0 and z1, y0 to y1) with gaps. */ static void wallX(Plan *p, float x0, float x1, float z0, float z1, float y0, float y1, const Gap *gaps, int n, Vec3 color, int kind) { float at = x0; for (int i = 0; i < n; i++) { /* (gaps given left to right) */ box(p, vec3(at, y0, z0), vec3(gaps[i].a, y1, z1), color, kind); box(p, vec3(gaps[i].a, y0, z0), vec3(gaps[i].b, gaps[i].y0, z1), color, kind); /* under it */ box(p, vec3(gaps[i].a, gaps[i].y1, z0), vec3(gaps[i].b, y1, z1), color, kind); /* over it */ at = gaps[i].b; } box(p, vec3(at, y0, z0), vec3(x1, y1, z1), color, kind); } /* The same along z. */ static void wallZ(Plan *p, float z0, float z1, float x0, float x1, float y0, float y1, const Gap *gaps, int n, Vec3 color, int kind) { float at = z0; for (int i = 0; i < n; i++) { box(p, vec3(x0, y0, at), vec3(x1, y1, gaps[i].a), color, kind); box(p, vec3(x0, y0, gaps[i].a), vec3(x1, gaps[i].y0, gaps[i].b), color, kind); box(p, vec3(x0, gaps[i].y1, gaps[i].a), vec3(x1, y1, gaps[i].b), color, kind); at = gaps[i].b; } box(p, vec3(x0, y0, at), vec3(x1, y1, z1), color, kind); } /* A locked door drawn on a wall face, and a thin solid piece in front of it that says what it is (clicked: "locked"). face: the way it faces. */ static void lockedDoor(Plan *p, Vec3 foot, Vec3 face, float w, const char *name) { opening(p, OPENING_DOOR, foot, face, w, DOOR_H - 0.1f); Vec3 along = vec3(face.z, 0.0f, -face.x); Vec3 a = vec3_add(foot, vec3_scale(along, -w * 0.5f)); Vec3 b = vec3_add(foot, vec3_add(vec3_scale(along, w * 0.5f), vec3_scale(face, 0.06f))); Vec3 min = vec3(fminf(a.x, b.x), foot.y, fminf(a.z, b.z)), max = vec3(fmaxf(a.x, b.x), foot.y + DOOR_H - 0.1f, fmaxf(a.z, b.z)); Object *o = solid(p, min, max, SOLID, name); if (o) o->variant = 1; } /* Windows along a wall face at z (facing +z if side > 0), at these x, at height y. */ static void windowsX(Plan *p, float z, float side, const float *xs, int n, float y, float w, float h, bool inside_too, float inner_z) { for (int i = 0; i < n; i++) { opening(p, OPENING_WINDOW, vec3(xs[i], y, z), side > 0.0f ? FACE_PZ : FACE_NZ, w, h); if (inside_too) opening(p, OPENING_PANE, vec3(xs[i], y, inner_z), side > 0.0f ? FACE_NZ : FACE_PZ, w, h); } } /* ---------- The left half: entrance, corridor, toilet, kitchen, locked rooms ---------- */ #define DOOR_X -7.6f /* the front door (left half), by your garages */ #define VEST_X0 -9.6f /* the entrance: ground level */ #define VEST_X1 -5.75f #define STAIR_X1 -8.4f /* the four steps up, on the left */ #define HALL_Z 1.4f /* the ground floor starts here (towards the back) */ #define CORR_Z0 -0.75f /* the corridor */ #define CORR_Z1 0.75f #define TOILET_X0 -5.6f #define KITCHEN_X0 -3.6f #define LIVING_X0 -5.0f #define GARAGE_X1 -9.75f /* your garages stand in front up to here */ #define WELL_X0 -6.95f /* the stairs down: from the landing at the front (WELL_TOP_Z), going back */ #define WELL_TOP_Z 3.9f /* (a landing 0.8 m deep in front of it: room to step round the rail) */ static void leftHalf(Plan *p) { const float x0 = -HALF_LEN, z0 = LEFT_Z0, z1 = LEFT_Z1; /* Outer walls: the front with the door; the back; the left end; the wall between the halves */ Gap door = { DOOR_X - 0.5f, DOOR_X + 0.5f, 0.0f, DOOR_H }; wallX(p, x0, -P, z1 - T, z1, 0.0f, WALL_TOP, &door, 1, PLASTER, EAVES_WALL); wallX(p, x0, -P, z0, z0 + T, 0.0f, WALL_TOP, NULL, 0, PLASTER, EAVES_WALL); wallZ(p, z0 + T, z1 - T, x0, x0 + T, 0.0f, WALL_TOP, NULL, 0, PLASTER, EAVES_WALL); Gap through = { CORR_Z0, CORR_Z1, FL, FL + DOOR_H }; wallZ(p, z0, RIGHT_Z1, -P, P, 0.0f, WALL_TOP, &through, 1, PLASTER, SOLID); /* Plinth paint round the outside (the basement), and the front door */ box(p, vec3(GARAGE_X1, 0.0f, z1), vec3(DOOR_X - 0.6f, FL - 0.05f, z1 + 0.02f), PLINTH, LOOK_ONLY); box(p, vec3(DOOR_X + 0.6f, 0.0f, z1), vec3(-P, FL - 0.05f, z1 + 0.02f), PLINTH, LOOK_ONLY); box(p, vec3(x0, 0.0f, z0 - 0.02f), vec3(-P, FL - 0.05f, z0), PLINTH, LOOK_ONLY); opening(p, OPENING_FRAME, vec3(DOOR_X, 0.0f, z1), FACE_PZ, 1.0f, DOOR_H); opening(p, OPENING_FRAME, vec3(DOOR_X, 0.0f, z1 - T), FACE_NZ, 1.0f, DOOR_H); /* Floors: the entrance on the ground, everything else four steps up */ box(p, vec3(VEST_X0, -0.06f, HALL_Z), vec3(WELL_X0, 0.02f, z1 - T), TILES, SOLID); box(p, vec3(WELL_X0, -0.3f, WELL_TOP_Z), vec3(VEST_X1, 0.02f, z1 - T), TILES, SOLID); /* the landing at the top of the stairs down */ box(p, vec3(x0 + T, 0.0f, z0 + T), vec3(-P, FL, HALL_Z), FLOORING, SOLID); /* back: corridor and rooms */ box(p, vec3(TOILET_X0, 0.0f, HALL_Z), vec3(-P, FL, z1 - T), FLOORING, SOLID); /* toilet, kitchen */ box(p, vec3(x0 + T, 0.0f, HALL_Z), vec3(GARAGE_X1, FL, z1 - T), FLOORING, SOLID); /* (a closed store room) */ for (int k = 0; k < 4; k++) /* the steps up, on the left */ box(p, vec3(VEST_X0, 0.0f, HALL_Z + 1.2f - 0.3f * (float)(k + 1)), vec3(STAIR_X1, STEP_H * (float)(k + 1), HALL_Z + 1.2f - 0.3f * (float)k), STEPS, SOLID); box(p, vec3(STAIR_X1, FL, HALL_Z), vec3(VEST_X1, FL + 0.95f, HALL_Z + 0.08f), RAIL, SOLID); /* the rail along the edge */ box(p, vec3(STAIR_X1, 0.0f, HALL_Z), vec3(VEST_X1, FL, HALL_Z + 0.02f), PLINTH, LOOK_ONLY); box(p, vec3(x0 + T, CEIL, z0 + T), vec3(-P, CEIL + SLAB, z1 - T), CEILING, SOLID); /* The entrance: its walls; on the right the stairs down to the basement (a rail along them) */ wallZ(p, CORR_Z0, z1 - T, GARAGE_X1, VEST_X0, 0.0f, CEIL, NULL, 0, INSIDE, SOLID); wallZ(p, HALL_Z, z1 - T, VEST_X1, TOILET_X0, 0.0f, CEIL, NULL, 0, INSIDE, SOLID); box(p, vec3(WELL_X0 - 0.05f, 0.0f, HALL_Z), vec3(WELL_X0, 0.95f, WELL_TOP_Z), RAIL, SOLID); box(p, vec3(WELL_X0 - 0.06f, 0.0f, WELL_TOP_Z - 0.06f), vec3(WELL_X0 + 0.01f, 0.97f, WELL_TOP_Z), RAIL, LOOK_ONLY); /* (its post) */ /* The corridor's walls: on the right toilet and kitchen (open), on the left two locked rooms */ Gap rooms[2] = { { -5.0f, -4.2f, FL, FL + DOOR_H }, { -2.5f, -1.5f, FL, FL + DOOR_H } }; wallX(p, TOILET_X0, -P, CORR_Z1, CORR_Z1 + P, FL, CEIL, rooms, 2, INSIDE, SOLID); opening(p, OPENING_FRAME, vec3(-4.6f, FL, CORR_Z1), FACE_NZ, 0.8f, DOOR_H); opening(p, OPENING_FRAME, vec3(-2.0f, FL, CORR_Z1), FACE_NZ, 1.0f, DOOR_H); wallZ(p, CORR_Z1 + P, z1 - T, KITCHEN_X0 - P, KITCHEN_X0, FL, CEIL, NULL, 0, INSIDE, SOLID); /* toilet | kitchen */ wallX(p, x0 + T, GARAGE_X1, CORR_Z1, CORR_Z1 + P, FL, CEIL, NULL, 0, INSIDE, SOLID); /* the store room */ wallX(p, GARAGE_X1, -P, CORR_Z0 - P, CORR_Z0, FL, CEIL, NULL, 0, INSIDE, SOLID); wallZ(p, z0 + T, CORR_Z0 - P, LIVING_X0 - P, LIVING_X0, FL, CEIL, NULL, 0, INSIDE, SOLID); lockedDoor(p, vec3(-7.2f, FL, CORR_Z0), FACE_PZ, 0.9f, "A locked room"); lockedDoor(p, vec3(-2.6f, FL, CORR_Z0), FACE_PZ, 0.9f, "Living room"); /* The toilet: a little basin (the toilet itself: see furnish) */ box(p, vec3(-4.2f, FL + 0.8f, 4.15f), vec3(-3.75f, FL + 0.95f, 4.6f), PORCELAIN, LOOK_ONLY); /* Windows: the front (behind your garages there are none), the back; basement windows low down */ const float front_low[] = { -4.6f, -1.9f }, front_up[] = { -8.6f, -5.5f, -2.2f }; windowsX(p, z1, 1.0f, &front_low[1], 1, FL + 1.45f, 1.0f, 1.2f, true, z1 - T); opening(p, OPENING_WINDOW, vec3(-4.6f, FL + 1.8f, z1), FACE_PZ, 0.6f, 0.6f); /* the toilet's */ opening(p, OPENING_PANE, vec3(-4.6f, FL + 1.8f, z1 - T), FACE_NZ, 0.6f, 0.6f); windowsX(p, z1, 1.0f, front_up, 3, CEIL + 1.5f, 1.0f, 1.2f, false, 0.0f); windowsX(p, z1, 1.0f, front_low, 2, 0.35f, 0.8f, 0.36f, false, 0.0f); float back[5]; for (int i = 0; i < 5; i++) back[i] = x0 + 1.8f + 3.6f * (float)i; windowsX(p, z0, -1.0f, back, 5, FL + 1.45f, 1.0f, 1.2f, false, 0.0f); windowsX(p, z0, -1.0f, back, 5, CEIL + 1.5f, 1.0f, 1.2f, false, 0.0f); windowsX(p, z0, -1.0f, back, 5, 0.35f, 0.8f, 0.36f, false, 0.0f); /* One roof over both halves: the left one's ridge runs on into the right one's roof (turned) till they meet */ const float rise = (z1 - z0) * ROOF_PITCH, into = HALF_LEN * 0.5f * rise / RIGHT_ROOF; const float len = HALF_LEN - 0.35f; /* (its verge just reaches the middle wall: the valleys go on from there) */ p->roofs[p->n_roofs++] = (ShapeRoof){ vec3(-HALF_LEN + len * 0.5f, 0.0f, (z0 + z1) * 0.5f), len, z1 - z0, WALL_TOP, rise, BUILDING_PLASTER_DARK, false, 0.35f + into }; } /* ---------- The right half: the big room; the porch in front ---------- */ #define RDOOR_X 11.0f /* the door from the porch */ #define PSTEP_X 13.8f /* the porch steps: up from here, going left */ static void rightHalf(Plan *p) { const float x1 = HALF_LEN, z0 = RIGHT_Z0, z1 = RIGHT_Z1; /* (the ridge runs front to back: gables at the front and back, the eaves over the right end) */ Gap door = { RDOOR_X - 0.5f, RDOOR_X + 0.5f, FL, FL + DOOR_H }; wallX(p, P, x1, z1 - T, z1, 0.0f, WALL_TOP, &door, 1, PLASTER, SOLID); wallX(p, P, x1, z0, z0 + T, 0.0f, WALL_TOP, NULL, 0, PLASTER, SOLID); wallZ(p, z0 + T, z1 - T, x1 - T, x1, 0.0f, WALL_TOP, NULL, 0, PLASTER, EAVES_WALL); box(p, vec3(P, 0.0f, z0 - 0.02f), vec3(x1, FL - 0.05f, z0), PLINTH, LOOK_ONLY); opening(p, OPENING_FRAME, vec3(RDOOR_X, FL, z1), FACE_PZ, 1.0f, DOOR_H); opening(p, OPENING_FRAME, vec3(RDOOR_X, FL, z1 - T), FACE_NZ, 1.0f, DOOR_H); box(p, vec3(P, 0.0f, z0 + T), vec3(x1 - T, FL, z1 - T), FLOORING, SOLID); box(p, vec3(P, CEIL, z0 + T), vec3(x1 - T, CEIL + SLAB, z1 - T), CEILING, SOLID); opening(p, OPENING_FRAME, vec3(P, FL, 0.0f), FACE_PX, CORR_Z1 - CORR_Z0, DOOR_H); /* from the corridor */ opening(p, OPENING_FRAME, vec3(-P, FL, 0.0f), FACE_NX, CORR_Z1 - CORR_Z0, DOOR_H); /* Windows: the front (the right ones into the porch), the back, basement windows at the back */ const float front[] = { 2.5f, 6.0f, 13.0f, 16.0f }, up[] = { 2.3f, 5.5f, 9.0f, 12.5f, 16.0f }; windowsX(p, z1, 1.0f, front, 4, FL + 1.45f, 1.0f, 1.2f, true, z1 - T); windowsX(p, z1, 1.0f, up, 5, CEIL + 1.5f, 1.0f, 1.2f, false, 0.0f); float back[5]; for (int i = 0; i < 5; i++) back[i] = 1.8f + 3.6f * (float)i; windowsX(p, z0, -1.0f, back, 5, FL + 1.45f, 1.0f, 1.2f, true, z0 + T); windowsX(p, z0, -1.0f, back, 5, CEIL + 1.5f, 1.0f, 1.2f, false, 0.0f); windowsX(p, z0, -1.0f, back, 5, 0.35f, 0.8f, 0.36f, false, 0.0f); box(p, vec3(P, 0.0f, z1), vec3(PORCH_X0, FL - 0.05f, z1 + 0.02f), PLINTH, LOOK_ONLY); /* (left of the porch: the plinth shows) */ /* The porch (the right half of the front): its front wall with windows, a closed left end, the way in at the right end */ const float pz0 = z1, pz1 = PORCH_Z1; wallX(p, PORCH_X0, x1, pz1 - T, pz1, 0.0f, PORCH_TOP, NULL, 0, PLASTER, SOLID); wallZ(p, pz0, pz1 - T, PORCH_X0, PORCH_X0 + T, 0.0f, PORCH_TOP, NULL, 0, PLASTER, SOLID); Gap in = { pz0 + 0.15f, pz1 - T - 0.1f, 0.0f, DOOR_H }; wallZ(p, pz0, pz1 - T, x1 - T, x1, 0.0f, PORCH_TOP, &in, 1, PLASTER, SOLID); opening(p, OPENING_FRAME, vec3(x1, 0.0f, (in.a + in.b) * 0.5f), FACE_PX, in.b - in.a, DOOR_H); box(p, vec3(PORCH_X0 - 0.1f, PORCH_TOP, pz0), vec3(x1 + 0.1f, PORCH_TOP + 0.18f, pz1 + 0.2f), PORCH_ROOF, SOLID); box(p, vec3(PORCH_X0, 0.0f, pz1), vec3(x1, 0.5f, pz1 + 0.02f), PLINTH, LOOK_ONLY); const float pw[] = { 11.2f, 15.2f }; windowsX(p, pz1, 1.0f, pw, 2, 1.65f, 1.0f, 1.0f, true, pz1 - T); /* its floor: on the ground at the way in, four steps up to the landing at the door */ box(p, vec3(PSTEP_X, -0.06f, pz0), vec3(x1 - T, 0.02f, pz1 - T), TILES, SOLID); for (int k = 0; k < 4; k++) box(p, vec3(PSTEP_X - 0.3f * (float)(k + 1), 0.0f, pz0), vec3(PSTEP_X - 0.3f * (float)k, STEP_H * (float)(k + 1), pz1 - T), STEPS, SOLID); box(p, vec3(PORCH_X0 + T, 0.0f, pz0), vec3(PSTEP_X - 1.2f, FL, pz1 - T), TILES, SOLID); box(p, vec3(PORCH_X0 + T, PORCH_TOP - 0.05f, pz0), vec3(x1 - T, PORCH_TOP, pz1 - T), CEILING, LOOK_ONLY); ShapeRoof r = { vec3(HALF_LEN * 0.5f, 0.0f, (z0 + z1) * 0.5f), z1 - z0, HALF_LEN, WALL_TOP, RIGHT_ROOF, BUILDING_PLASTER_DARK, true, 0.0f }; p->roofs[p->n_roofs++] = r; } /* The roofs: a slab under each slope (you can get onto them from a ladder). */ static void roofSlabs(Plan *p, const ShapeRoof *r) { const float half_w = r->width * 0.5f, angle = atanf(r->roof_height / half_w); const float slope = sqrtf(half_w * half_w + r->roof_height * r->roof_height), thick = 0.25f; for (int side = -1; side <= 1; side += 2) { const float s = (float)side; /* ridge along x: the slopes to -z and +z; along z (across): to -x and +x */ Vec3 normal = r->across ? vec3(s * sinf(angle), cosf(angle), 0.0f) : vec3(0.0f, cosf(angle), s * sinf(angle)); Vec3 off = r->across ? vec3(s * half_w * 0.5f, 0.0f, 0.0f) : vec3(0.0f, 0.0f, s * half_w * 0.5f); Vec3 surface = vec3_add(vec3(r->center.x, r->wall_top + r->roof_height * 0.5f, r->center.z), off); Vec3 center = toWorld(p, vec3_sub(surface, vec3_scale(normal, thick * 0.5f))); Vec3 size = r->across ? vec3(slope, thick, r->length) : vec3(r->length, thick, slope); Object *o = Object_create(OBJ_WALL, PALETTE_GROUND, vec3(center.x, center.y - thick * 0.5f, center.z), size, 1000.0f); if (!o) { p->ok = false; return; } Object_setRotation(o, r->across ? vec3(0.0f, p->yaw, s * angle) : vec3(-s * angle, p->yaw, 0.0f)); o->variant = 3; /* (a roof) */ if (!World_add(p->world, o)) { p->ok = false; return; } } } /* ---------- The basement: under the left half, down from the entrance ---------- */ #define BASE_Y -2.45f /* its floor */ #define BASE_X0 -9.6f /* the big part: under the corridor and the rooms at the back ... */ #define BASE_X1 -0.15f #define BASE_Z0 -4.7f #define BASE_Z1 1.4f /* (= HALL_Z) */ #define UNDER_Z1 4.7f /* ... and under the stairs, toilet and kitchen */ #define STAIR_STEPS 14 #define STAIR_TREAD 0.23f #define BW 0.25f /* its walls */ static const Vec3 OLD_BRICK = { 0.62f, 0.36f, 0.27f }; static const Vec3 EARTH_FLOOR = { 0.42f, 0.38f, 0.33f }; static const Vec3 CONCRETE = { 0.60f, 0.58f, 0.54f }; /* A few bricks loose on a wall face (sticking out a bit, darker or paler) and fallen to the floor. */ static void looseBricks(Plan *p, Vec3 min, Vec3 max, int n) { const bool along_x = max.x - min.x > max.z - min.z; for (int i = 0; i < n; i++) { float t = rnd(p), y = min.y + 0.3f + rnd(p) * (max.y - min.y - 0.5f), shade = 0.8f + 0.35f * rnd(p); Vec3 c = vec3_scale(OLD_BRICK, shade); float side = rnd(p) < 0.5f ? -1.0f : 1.0f; if (along_x) { float x = min.x + 0.2f + t * (max.x - min.x - 0.4f), z = side > 0.0f ? max.z : min.z; box(p, vec3(x - 0.11f, y, z - 0.02f), vec3(x + 0.11f, y + 0.07f, z + 0.02f), c, LOOK_ONLY); } else { float z = min.z + 0.2f + t * (max.z - min.z - 0.4f), x = side > 0.0f ? max.x : min.x; box(p, vec3(x - 0.02f, y, z - 0.11f), vec3(x + 0.02f, y + 0.07f, z + 0.11f), c, LOOK_ONLY); } } } static void basement(Plan *p) { const float rise = -BASE_Y / (float)STAIR_STEPS; /* Floor (packed earth, the ground itself goes down to it: see Terrain_addPit) and the ceiling seen from below */ box(p, vec3(BASE_X0, BASE_Y - 0.04f, BASE_Z0), vec3(BASE_X1, BASE_Y, BASE_Z1), EARTH_FLOOR, LOOK_ONLY); box(p, vec3(WELL_X0, BASE_Y - 0.04f, BASE_Z1), vec3(BASE_X1, BASE_Y, UNDER_Z1), EARTH_FLOOR, LOOK_ONLY); box(p, vec3(BASE_X0, -0.03f, BASE_Z0), vec3(BASE_X1, 0.0f, BASE_Z1), CONCRETE, LOOK_ONLY); box(p, vec3(VEST_X1, -0.03f, BASE_Z1), vec3(BASE_X1, 0.0f, UNDER_Z1), CONCRETE, LOOK_ONLY); /* The walls round it: old brick, earth behind */ p->brick = true; box(p, vec3(BASE_X0 - BW, BASE_Y, BASE_Z0 - BW), vec3(BASE_X1 + BW, 0.0f, BASE_Z0), OLD_BRICK, SOLID); /* back */ box(p, vec3(BASE_X0 - BW, BASE_Y, BASE_Z0), vec3(BASE_X0, 0.0f, BASE_Z1 + BW), OLD_BRICK, SOLID); /* left */ box(p, vec3(BASE_X1, BASE_Y, BASE_Z0), vec3(BASE_X1 + BW, 0.0f, UNDER_Z1 + BW), OLD_BRICK, SOLID); /* right */ box(p, vec3(BASE_X0, BASE_Y, BASE_Z1), vec3(WELL_X0, 0.0f, BASE_Z1 + BW), OLD_BRICK, SOLID); /* under the entrance */ box(p, vec3(WELL_X0 - BW, BASE_Y, BASE_Z1 + BW), vec3(WELL_X0, 0.0f, UNDER_Z1 + BW), OLD_BRICK, SOLID); /* beside the stairs */ box(p, vec3(WELL_X0, BASE_Y, UNDER_Z1), vec3(BASE_X1, 0.0f, UNDER_Z1 + BW), OLD_BRICK, SOLID); /* front */ /* Inside: loose brick walls, open (no doors) */ Gap side_way = { 2.4f, 3.4f, BASE_Y, 0.0f }; /* (up to the ceiling: no lintel hanging there) */ wallZ(p, BASE_Z1 + 0.15f, UNDER_Z1, VEST_X1, TOILET_X0, BASE_Y, 0.0f, &side_way, 1, OLD_BRICK, SOLID); /* the stairs | under the kitchen */ Gap through = { -3.6f, -2.6f, BASE_Y, -0.35f }; wallX(p, TOILET_X0, BASE_X1, BASE_Z1, BASE_Z1 + 0.15f, BASE_Y, 0.0f, &through, 1, OLD_BRICK, SOLID); wallZ(p, BASE_Z0, -1.4f, -5.0f, -4.82f, BASE_Y, 0.0f, NULL, 0, OLD_BRICK, SOLID); /* two rooms at the back */ wallX(p, BASE_X0, -7.6f, -1.6f, -1.42f, BASE_Y, -0.25f, NULL, 0, OLD_BRICK, SOLID); /* a nook on the left */ p->brick = false; looseBricks(p, vec3(-5.0f, BASE_Y, BASE_Z0), vec3(-4.82f, 0.0f, -1.4f), 6); looseBricks(p, vec3(BASE_X0, BASE_Y, -1.6f), vec3(-7.6f, -0.25f, -1.42f), 4); looseBricks(p, vec3(TOILET_X0, BASE_Y, BASE_Z1), vec3(BASE_X1, 0.0f, BASE_Z1 + 0.15f), 5); for (int i = 0; i < 9; i++) { /* fallen out */ float x = -8.8f + 8.0f * rnd(p), z = BASE_Z0 + 0.3f + 5.2f * rnd(p); box(p, vec3(x, BASE_Y, z), vec3(x + 0.22f, BASE_Y + 0.07f, z + 0.1f), vec3_scale(OLD_BRICK, 0.85f + 0.3f * rnd(p)), LOOK_ONLY); } /* The stairs: from the landing in the entrance down towards the back */ for (int k = 0; k < STAIR_STEPS - 1; k++) box(p, vec3(WELL_X0, BASE_Y, WELL_TOP_Z - STAIR_TREAD * (float)(k + 1)), vec3(VEST_X1, -rise * (float)(k + 1), WELL_TOP_Z - STAIR_TREAD * (float)k), STEPS, SOLID); } /* What stands inside: the meeting table in the big room, the kitchen, shelves in the basement. */ static void furnish(Plan *p) { const float fl = FL, b = BASE_Y; const Vec3 chair = { 0.45f, 0.9f, 0.45f }, shelf = { 1.2f, 1.8f, 0.4f }; struct { FurnitureKind kind; Vec3 at; Vec3 size; Vec3 face; } items[] = { { FURN_TABLE, { 9.5f, fl, 0.8f }, { 4.2f, 0.78f, 1.6f }, FACE_PZ }, /* the big table */ { FURN_CHAIR, { 8.0f, fl, 2.15f }, chair, FACE_NZ }, /* (all facing it) */ { FURN_CHAIR, { 9.5f, fl, 2.15f }, chair, FACE_NZ }, { FURN_CHAIR, { 11.0f, fl, 2.15f }, chair, FACE_NZ }, { FURN_CHAIR, { 8.0f, fl, -0.55f }, chair, FACE_PZ }, { FURN_CHAIR, { 9.5f, fl, -0.55f }, chair, FACE_PZ }, { FURN_CHAIR, { 11.0f, fl, -0.55f }, chair, FACE_PZ }, { FURN_CHAIR, { 6.85f, fl, 0.8f }, chair, FACE_PX }, { FURN_CHAIR, { 12.15f, fl, 0.8f }, chair, FACE_NX }, { FURN_TOILET, { -5.05f, fl, 4.05f }, { 0.42f, 0.8f, 0.8f }, FACE_NZ }, /* the toilet: its cistern against the front wall */ { FURN_COUNTER, { -1.9f, fl, 4.4f }, { 3.0f, 0.9f, 0.6f }, FACE_NZ }, /* the kitchen: the counter along the front wall */ { FURN_STOVE, { -0.45f, fl, 2.6f }, { 0.6f, 0.9f, 0.6f }, FACE_NX }, /* ... the stove against the wall to the big room */ { FURN_SHELF, { -3.4f, fl, 2.3f }, shelf, FACE_PX }, /* ... a shelf against the toilet's wall */ { FURN_TABLE, { -1.9f, fl, 2.4f }, { 1.2f, 0.75f, 0.8f }, FACE_PZ }, { FURN_CHAIR, { -1.9f, fl, 1.65f }, chair, FACE_PZ }, { FURN_CHAIR, { -1.9f, fl, 3.15f }, chair, FACE_NZ }, { FURN_SHELF, { -8.4f, b, BASE_Z0 + 0.2f }, shelf, FACE_PZ }, /* the basement */ { FURN_SHELF, { -6.4f, b, BASE_Z0 + 0.2f }, shelf, FACE_PZ }, { FURN_SHELF, { -3.6f, b, BASE_Z0 + 0.2f }, shelf, FACE_PZ }, { FURN_SHELF, { -1.3f, b, BASE_Z0 + 0.2f }, shelf, FACE_PZ }, { FURN_SHELF, { BASE_X0 + 0.2f, b, -3.2f }, shelf, FACE_PX }, { FURN_SHELF, { -0.35f, b, -1.0f }, shelf, FACE_NX }, { FURN_SHELF, { -2.0f, b, UNDER_Z1 - 0.2f }, shelf, FACE_NZ }, { FURN_SHELF, { -4.0f, b, UNDER_Z1 - 0.2f }, shelf, FACE_NZ }, }; for (size_t i = 0; i < sizeof(items) / sizeof(items[0]) && p->ok; i++) { Object *o = Object_createFurniture(items[i].kind, toWorld(p, items[i].at), items[i].size, p->yaw + yaw_facing(items[i].face)); if (!o || !World_add(p->world, o)) p->ok = false; } /* Urinellas in a little carton on the toilet's floor, by the wall */ Object *urinellas = p->ok ? Object_create(OBJ_SUPPLY, PALETTE_GROUND, toWorld(p, vec3(-3.85f, fl + 0.09f, 3.2f)), vec3(0.3f, 0.18f, 0.2f), 100.0f) : NULL; if (urinellas) { urinellas->dispenses = OBJ_URINELLA; Object_setRotation(urinellas, vec3(0.0f, p->yaw, 0.0f)); if (!World_add(p->world, urinellas)) p->ok = false; } else p->ok = false; } /* The ground goes down to the basement floor under it, and isn't drawn inside the walls at all (it showed through the floors here and there). Rectangles: local min and max x, z, and the floor (the basement's first: the first one a point is in counts). */ static void digBasement(Plan *p) { const float rects[5][5] = { { BASE_X0, BASE_Z0, BASE_X1, BASE_Z1, BASE_Y }, { WELL_X0, BASE_Z1, BASE_X1, UNDER_Z1, BASE_Y }, { -HALF_LEN + T, LEFT_Z0 + T, -P, LEFT_Z1 - T, -0.06f }, /* (under the floors) */ { P, RIGHT_Z0 + T, HALF_LEN - T, RIGHT_Z1 - T, -0.06f }, { PORCH_X0 + T, RIGHT_Z1, HALF_LEN - T, PORCH_Z1 - T, -0.06f }, }; for (int i = 0; i < 5; i++) { Vec3 mid = toWorld(p, vec3((rects[i][0] + rects[i][2]) * 0.5f, 0.0f, (rects[i][1] + rects[i][3]) * 0.5f)); if (!Terrain_addPit(&p->world->terrain, mid, p->yaw, (rects[i][2] - rects[i][0]) * 0.5f, (rects[i][3] - rects[i][1]) * 0.5f, p->origin.y + rects[i][4])) p->ok = false; } } /* ---------- Your unfinished building: a brick shell under a roof ---------- */ #define SHELL_W 6.4f /* along x */ #define SHELL_D 6.7f /* along z (+z: the back, against the barn; -z: the front, to the extension) */ #define SHELL_WALL 2.9f #define SHELL_GAP 1.6f /* the way in: on its -x side (where the woodpile is) */ static const Vec3 BRICK = { 0.78f, 0.42f, 0.30f }; bool Shell_build(World *world, Vec3 origin, float yaw) { static Plan plan; Plan *p = &plan; memset(p, 0, sizeof(*p)); p->world = world; p->origin = origin; p->yaw = yaw; p->ok = true; const float x0 = -SHELL_W * 0.5f, x1 = SHELL_W * 0.5f, z0 = -SHELL_D * 0.5f, z1 = SHELL_D * 0.5f; p->brick = true; /* (the ridge runs front to back: gables front and back, the eaves over the sides) */ Gap front_hole[1] = { { -0.6f, 0.6f, 1.0f, 2.0f } }; /* window holes, no glass yet */ wallX(p, x0, x1, z0, z0 + T, 0.0f, SHELL_WALL, front_hole, 1, BRICK, SOLID); /* the front, to the extension */ wallX(p, x0, x1, z1 - T, z1, 0.0f, SHELL_WALL, NULL, 0, BRICK, SOLID); /* the back */ Gap door_side[2] = { { z0 + 0.9f, z0 + 0.9f + SHELL_GAP, 0.0f, 2.2f }, { 1.2f, 2.2f, 1.0f, 2.0f } }; wallZ(p, z0 + T, z1 - T, x0, x0 + T, 0.0f, SHELL_WALL, door_side, 2, BRICK, EAVES_WALL); /* the way in, and a window */ Gap holes[2] = { { -2.0f, -1.0f, 1.0f, 2.0f }, { 1.0f, 2.0f, 1.0f, 2.0f } }; wallZ(p, z0 + T, z1 - T, x1 - T, x1, 0.0f, SHELL_WALL, holes, 2, BRICK, EAVES_WALL); p->brick = false; box(p, vec3(x0 + T, 0.0f, z0 + T), vec3(x1 - T, 0.02f, z1 - T), (Vec3){ 0.55f, 0.50f, 0.44f }, LOOK_ONLY); /* bare concrete */ p->roofs[p->n_roofs++] = (ShapeRoof){ vec3(0.0f, 0.0f, 0.0f), SHELL_D, SHELL_W, SHELL_WALL, SHELL_W * ROOF_PITCH, BUILDING_BRICK, true, 0.0f }; roofSlabs(p, &p->roofs[0]); if (!p->ok) return false; Mesh mesh = Shapes_structure(p->slabs, p->n_slabs, p->openings, p->n_openings, p->roofs, p->n_roofs); if (!mesh.vao) return false; Object *look = Object_createStructure(mesh, origin, yaw, vec3(SHELL_W, SHELL_WALL, SHELL_D)); return look && World_add(world, look); } /* ---------- The shelter: a roof on posts in the yard, benches under it ---------- */ #define PAVILION_SIDE 3.0f /* m: a party pavilion, square */ #define PAVILION_EAVES 2.3f /* m: under the valance */ #define PAVILION_PEAK 3.1f /* m: the top of its roof */ #define PAVILION_COVER 2.5f /* m: the (unseen) roof that keeps the rain off, under the canvas */ bool Shelter_build(World *world, Vec3 origin, float yaw) { static Plan plan; Plan *p = &plan; memset(p, 0, sizeof(*p)); p->world = world; p->origin = origin; p->yaw = yaw; p->ok = true; const Vec3 CANVAS = { 0.93f, 0.93f, 0.9f }; const float h = PAVILION_SIDE * 0.5f, leg = 0.045f; for (int k = -1; k <= 1; k += 2) { /* two side by side along the yard */ float x = (float)k * (h + 0.02f); for (int i = -1; i <= 1; i += 2) /* the legs */ for (int j = -1; j <= 1; j += 2) { Vec3 at = vec3(x + (float)i * (h - leg), 0.0f, (float)j * (h - leg)); solid(p, vec3(at.x - leg, 0.0f, at.z - leg), vec3(at.x + leg, PAVILION_EAVES, at.z + leg), SOLID, NULL); } solid(p, vec3(x - h + 0.2f, PAVILION_COVER, -h + 0.2f), vec3(x + h - 0.2f, PAVILION_COVER + 0.04f, h - 0.2f), SOLID, NULL); Mesh mesh = Shapes_pavilion(PAVILION_SIDE, PAVILION_EAVES, PAVILION_PEAK, CANVAS); if (!mesh.vao) return false; Object *look = Object_createStructure(mesh, toWorld(p, vec3(x, 0.0f, 0.0f)), yaw, vec3(PAVILION_SIDE, PAVILION_PEAK, PAVILION_SIDE)); if (!look || !World_add(world, look)) return false; /* a beer table set under it: the table, a bench either side */ Object *table = Object_createFurniture(FURN_TABLE, toWorld(p, vec3(x, 0.0f, 0.0f)), vec3(2.2f, 0.77f, 0.5f), yaw); if (!table || !World_add(world, table)) return false; for (int s = -1; s <= 1; s += 2) { Object *bench = Object_createFurniture(FURN_BENCH, toWorld(p, vec3(x, 0.0f, (float)s * 0.62f)), vec3(2.2f, SEAT_HEIGHT, 0.25f), yaw); if (!bench || !World_add(world, bench)) return false; bench->table = table; } } return p->ok; } bool Manor_build(World *world, Vec3 origin, float yaw) { static Plan plan; /* (big: not on the stack) */ Plan *p = &plan; memset(p, 0, sizeof(*p)); p->world = world; p->origin = origin; p->yaw = yaw; p->ok = true; p->rng = 12345u; leftHalf(p); rightHalf(p); basement(p); digBasement(p); for (int i = 0; i < p->n_roofs; i++) roofSlabs(p, &p->roofs[i]); furnish(p); if (!p->ok) return false; Mesh mesh = Shapes_structure(p->slabs, p->n_slabs, p->openings, p->n_openings, p->roofs, p->n_roofs); if (!mesh.vao) return false; Object *look = Object_createStructure(mesh, origin, yaw, vec3(2.0f * HALF_LEN, WALL_TOP, PORCH_Z1 - LEFT_Z0)); return look && World_add(world, look); } /* ---------- Berlin: the flat in Grossbeerenstrasse and the Herberge ---------- Two rooms off the map (BERLIN_X: the site is out of sight from there), closed in, windows drawn on the walls as in the farm's house. */ #define ROOM_H 3.2f /* (an old Berlin house: high ceilings) */ static Object *furniture(Plan *p, FurnitureKind kind, Vec3 at, Vec3 size, Vec3 face) { Object *o = Object_createFurniture(kind, toWorld(p, at), size, p->yaw + yaw_facing(face)); if (!o || !World_add(p->world, o)) { p->ok = false; return NULL; } return o; } /* A closed room: floor, ceiling, four walls (x0..x1, z0..z1 inside), a locked door on its -z wall, windows on +z. */ static void room(Plan *p, float x0, float x1, float z0, float z1, Vec3 wall, Vec3 floor, const char *door) { box(p, vec3(x0 - T, -0.2f, z0 - T), vec3(x1 + T, 0.0f, z1 + T), floor, SOLID); box(p, vec3(x0, ROOM_H, z0), vec3(x1, ROOM_H + SLAB, z1), CEILING, SOLID); /* (inside the walls: touching their tops it wasn't drawn) */ wallX(p, x0 - T, x1 + T, z1, z1 + T, 0.0f, ROOM_H + SLAB, NULL, 0, wall, SOLID); wallX(p, x0 - T, x1 + T, z0 - T, z0, 0.0f, ROOM_H + SLAB, NULL, 0, wall, SOLID); wallZ(p, z0, z1, x0 - T, x0, 0.0f, ROOM_H + SLAB, NULL, 0, wall, SOLID); wallZ(p, z0, z1, x1, x1 + T, 0.0f, ROOM_H + SLAB, NULL, 0, wall, SOLID); float xs[4]; int n = (int)((x1 - x0) / 2.6f); if (n > 4) n = 4; for (int i = 0; i < n; i++) xs[i] = x0 + (x1 - x0) * ((float)i + 0.5f) / (float)n; for (int i = 0; i < n; i++) opening(p, OPENING_PANE, vec3(xs[i], 1.0f + 0.85f, z1), FACE_NZ, 1.1f, 1.7f); lockedDoor(p, vec3((x0 + x1) * 0.5f + 1.5f, 0.0f, z0), FACE_PZ, 1.0f, door); } bool Berlin_build(World *world) { static Plan plan; Plan *p = &plan; memset(p, 0, sizeof(*p)); p->world = world; p->origin = vec3(BERLIN_X, 0.0f, 0.0f); p->yaw = 0.0f; p->ok = true; const Vec3 WALL_FLAT = { 0.90f, 0.88f, 0.80f }, WALL_BAR = { 0.55f, 0.32f, 0.22f }, PLANKS = { 0.55f, 0.38f, 0.24f }, BOARDS = { 0.32f, 0.22f, 0.15f }; const Vec3 chair = { 0.45f, 0.9f, 0.45f }, shelf = { 1.2f, 1.9f, 0.4f }; /* The flat, 3rd floor: a big kitchen with the table in the middle */ room(p, -4.0f, 4.0f, -3.0f, 3.0f, WALL_FLAT, PLANKS, "The stairwell door - the meeting's here"); furniture(p, FURN_COUNTER, vec3(-3.6f, 0.0f, 0.4f), vec3(2.4f, 0.9f, 0.6f), FACE_PX); furniture(p, FURN_STOVE, vec3(-3.6f, 0.0f, -1.5f), vec3(0.6f, 0.9f, 0.6f), FACE_PX); furniture(p, FURN_SHELF, vec3(3.7f, 0.0f, 1.2f), shelf, FACE_NX); furniture(p, FURN_BENCH, vec3(3.6f, 0.0f, -1.6f), vec3(1.8f, SEAT_HEIGHT, 0.5f), FACE_NX); furniture(p, FURN_TABLE, vec3(0.0f, 0.0f, 0.0f), vec3(2.2f, 0.75f, 0.9f), FACE_PZ); for (int k = -1; k <= 1; k++) { furniture(p, FURN_CHAIR, vec3((float)k * 0.7f, 0.0f, -0.85f), chair, FACE_PZ); furniture(p, FURN_CHAIR, vec3((float)k * 0.7f, 0.0f, 0.85f), chair, FACE_NZ); } /* The Herberge: the bar, a long table with two benches, two small tables */ const float hx = BERLIN_BAR; room(p, hx - 6.0f, hx + 6.0f, -4.0f, 4.0f, WALL_BAR, BOARDS, "The door out to the street - the meeting's in here"); furniture(p, FURN_BAR, vec3(hx - 5.2f, 0.0f, 0.0f), vec3(4.5f, 1.1f, 0.6f), FACE_PX); for (int k = -1; k <= 1; k += 2) furniture(p, FURN_SHELF, vec3(hx - 5.75f, 0.0f, (float)k * 1.3f), shelf, FACE_PX); Object *table = furniture(p, FURN_TABLE, vec3(hx + 0.6f, 0.0f, 0.0f), vec3(4.4f, 0.77f, 0.9f), FACE_PZ); for (int s = -1; s <= 1; s += 2) { Object *bench = furniture(p, FURN_BENCH, vec3(hx + 0.6f, 0.0f, (float)s * 0.85f), vec3(4.4f, SEAT_HEIGHT, 0.3f), FACE_PZ); if (bench) bench->table = table; } for (int k = -1; k <= 1; k += 2) { furniture(p, FURN_TABLE, vec3(hx + 4.6f, 0.0f, (float)k * 2.4f), vec3(0.8f, 0.75f, 0.8f), FACE_PZ); furniture(p, FURN_CHAIR, vec3(hx + 4.6f, 0.0f, (float)k * 2.4f - (float)k * 0.75f), chair, (Vec3){ 0.0f, 0.0f, (float)k }); } if (!p->ok) return false; Mesh mesh = Shapes_structure(p->slabs, p->n_slabs, p->openings, p->n_openings, p->roofs, p->n_roofs); if (!mesh.vao) return false; Object *look = Object_createStructure(mesh, p->origin, 0.0f, vec3(BERLIN_BAR + 14.0f, ROOM_H, 10.0f)); return look && World_add(world, look); }