Геймдизайнер-телепат
0,0
рейтинг
9 октября 2013 в 15:22

Разработка → Процедурный генератор хрущёвок

Сидел я как-то дома, читал статью про хрущёвки и восторгался гением архитектора. Потом меня отпустило, и я подумал, что унылость и однообразие хрущёвок очень легко можно описать математически. Прямые углы, равные интервалы, минимум украшений — что может быть проще?

На самом деле, у хрущёвок существует несколько десятков модификаций, но некая основа, сущность хрущёвки всё равно прослеживается.

В общем, недолго думая, я сел и написал генератор хрущёвок на C# под Unity3d. Под катом описание работы алгоритма и размышления на тему uv-карт, сабмешей и шейдеров.

▣ Первая попытка. Тотальное программирование всей геометрии.


Для начала я попробовал сделать всё программно. Плюсов у такого подхода несколько: не нужно открывать редактор, меньше размер дистрибутива и проще манипулировать математической стороной моделей. Минусов нет, если вы быстро печатаете, у вас много бумаги и хорошее воображение. Вручную соединять вершины в треугольники не очень-то просто.

Для тех, кто не в курсе:
Вы наверное слышали, что трёхмерные модели состоят из полигонов. Под полигонами обычно понимают треугольники. Треугольники описывают с помощью вершин и связей между ними. Дополнительно к этой информации прикладывается список нормалей — векторов, которые подсказывают графическим движкам как нужно освещать модель.

Возьмём к примеру окно. Если оно абсолютно плоское и представляет собой просто квадратик в квадратике, то это уже выходит восемь вершин и десять треугольников. Если окно немного утопить в стену, то это ещё четыре вершины и ещё восемь треугольников. А если ещё добавить подоконник или ящик с цветами? Лично у меня от таких вычислений из ушей начинает валить пар.

Код для создания примитивного окошка
На вход подаётся четыре вершины панели с окном. Из произведения векторов находим нормаль. Высчитываем ещё четыре вершины для углов окна. Собираем всё в подходящие массивы, делаем из вершин треугольники. Распределяем треугольники по сабмешам. Про сабмеши читайте далее.

Mesh EntrancePanel(Vector3 vertex0, Vector3 vertex1, Vector3 vertex2, Vector3 vertex3)
{
    var normal = Vector3.Cross((vertex1 - vertex0), (vertex2 - vertex0)).normalized;
    var window0 = vertex0 + (vertex3 - vertex0) * 0.25f + (vertex1 - vertex0) * 0.25f;
    var window1 = vertex0 + (vertex3 - vertex0) * 0.25f + (vertex1 - vertex0) * 0.75f;
    var window2 = vertex0 + (vertex3 - vertex0) * 0.75f + (vertex1 - vertex0) * 0.75f;
    var window3 = vertex0 + (vertex3 - vertex0) * 0.75f + (vertex1 - vertex0) * 0.25f;

    var mesh = new Mesh
    {
        vertices = new[] {vertex0, vertex1, vertex2, vertex3,
                        window0, window1, window2, window3,
                        window0, window1, window2, window3},
        normals = new[] { normal, normal, normal, normal,
                        normal, normal, normal, normal,
                        normal, normal, normal, normal },
        uv = new[] {new Vector2(0, 0), new Vector2(0, 1), new Vector2(1, 1), new Vector2(1, 0),
                        new Vector2(0.25f, 0.25f), new Vector2(0.25f, 0.75f),
                        new Vector2(0.75f, 0.75f), new Vector2(0.75f, 0.25f),
                        new Vector2(0, 0), new Vector2(0, 1), new Vector2(1, 1), new Vector2(1, 0)},
        triangles = new[]
                {
                    0, 1, 4,
                    4, 1, 5,
                    1, 2, 5,
                    5, 2, 6,
                    2, 3, 6,
                    6, 3, 7,
                    3, 0, 7,
                    7, 0, 4,

                    8, 9, 10,
                    10, 11, 8},
        subMeshCount = 2
    };
    mesh.SetTriangles(new[] { 0, 1, 4,
                                4, 1, 5,
                                1, 2, 5,
                                5, 2, 6,
                                2, 3, 6,
                                6, 3, 7,
                                3, 0, 7,
                                7, 0, 4}, 0);
    mesh.SetTriangles(new[] { 8, 9, 10, 10, 11, 8 }, 1);
    return mesh;
}

Вариант интересный, но после того, как я потратил кучу времени на создание стенки, окна, парадной двери и остеклённого балкона, мне вдруг взгрустнулось, и я решил зайти с другой стороны.

▣ Вторая попытка. Всё через редактор, с сабмешами и множеством материалов


Вообще говоря, движку совершенно безразлично происхождение вершин, треугольников и нормалей. Две модельки, одна из которых «родилась» в коде, а другая в Blender'е, замечательно подружатся и будут работать одинаково. Более того, их можно слить в одну модель с сабмешами (подмоделями? подсетками? подзацеплениями?), все трансформации которой будут влиять на сабмеши. Сабмеши это просто дополнительные списки с индексами вершин, ничего более.

На каждый из сабмешей можно повесить свой материал, который будет делать стёкла блестящими, а стены шершавыми. Идея вполне сносная, вот только каждый материал это дополнительный вызов шейдера, а слежка за индексами сабмешей оказалась невероятно муторной задачей. Я немного поигрался с сабмешами и забросил эту затею.

▣ Третья попытка. Cамописный шейдер и текстурные карты


Видеокарты любят, когда им скармливают полигоны и текстуры большими порциями. Например, им гораздо проще проглотить одну модель с миллионом треугольников, чем миллион моделей с одним треугольником. Такой же принцип применим к текстурам. Когда кучу картинок склеивают в одну большую получается текстурный атлас. Часто делают несколько текстурных атласов: один для цвета, другой для освещения, третий для отражений.

Я решил написать свой шейдер, благо в Unity это просто, и использовал для текстурирования моделей единый атлас. Шейдер принимает на вход этот атлас и дополнительную карту блестящих поверхностей, в которую я занёс все окна.

Код шейдера для хрущёвки
Код ниже это обычный Specular шейдер, идущий в комплекте с Unity, в котором в отдельную карту вынесены окна и добавлен параметр для смены их цвета.

Shader "Custom/Khrushchyovka" {
	Properties {
		_Color ("Main Color", Color) = (1,1,1,1)
		_MainTex ("Base (RGBA)", 2D) = "white" {}
		_GlassColor ("Glass Color", Color) = (0.5, 0.5, 0.5, 1)
		_Shininess ("Shininess", Range (0.01, 1)) = 0.078125
		_SpecColor ("Specular Color", Color) = (0.5, 0.5, 0.5, 1)
		_SpecTex ("Specular (RGB)", 2D) = "gray" {}
	}
	SubShader {
		Tags { "RenderType"="Opaque" }
		LOD 200
		
		CGPROGRAM
		#pragma surface surf BlinnPhong

		sampler2D _MainTex, _SpecTex;
		fixed4 _Color, _GlassColor;
		half _Shininess;

		struct Input {
			float2 uv_MainTex;
		};

		void surf (Input IN, inout SurfaceOutput o) {
			half4 main = tex2D (_MainTex, IN.uv_MainTex);
			half4 spec = tex2D(_SpecTex, IN.uv_MainTex);
			o.Albedo = main.rgb * _Color.rgb + spec.rgb * _GlassColor.rgb;
			o.Gloss = spec.rgb;
			o.Specular = _Shininess;
		}
		ENDCG
	} 
	FallBack "Diffuse"
}


▣ Возводим стенку


Теперь самое интересное — сборка здания. Проще всего начать с одной стены. Чтобы сделать из нескольких панелей стену, их нужно расставить рядом. В предыдущей статье я упоминал CombineMeshes, используемый для объединения моделей. Вместе с моделями ему можно скармливать матрицы трансформирования, с помощью которых можно двигать, вращать и менять размер моделей. Логика простая: в цикле набираем нужное количество моделей, каждую сдвигаем на определённое расстояние, получаем непрерывную стену. Если нужна стена определённой размера, то просто делим его на длину одной панели и узнаём необходимое количество панелей.

После непродолжительного гугления оказалось, что панели в хрущёвках бывают разного размера. Чтобы сильно не заморачиваться, я сделал все плиты двух размеров: длиной 2,5 м. и 3 м. На самом деле размеры должны быть немного другие, но у меня сходу не получилось найти толковой документации.

С двумя разными плитами заполнить интервал заданной длины гораздо сложнее. У этой задачи есть своё название — Subset sum problem. Вариантов решения этой проблемы много, я выбрал простой рекурсивный алгоритм.

Изначально есть массив с доступными длинами панелей и отрезок, который необходимо заполнить. В результате работы алгоритма получается другой массив, числа в котором означают количество необходимых панелей с длиной по совпадающему индексу из первого массива. То есть первый массив выглядит так: {3, 2.5f}. А второй для отрезка 11 метров выглядит так: {2, 2}. Отмечу ещё, что массив с панелями отсортирован в убывающем порядке.

Отрезок для заполнения целочисленно делим на длину самой большой панели, результат добавляем во второй массив, остаток записываем в переменную. Если остаток равен нулю, то задача выполнена, а результат во втором массиве. Если остаток больше нуля, это значит, что самая большая панель его перекрывает, выкидываем одну большую панель, добавляем её длину к остатку и повторяем первую операцию с остатком и следующей панелью в списке. Выкидываем большие панели до тех пор, пока самой большой панелью во втором массиве не окажется самая маленькая в первом, или остаток не станет равным нулю.

Код алгоритма
В дополнение к вышесказанному добавлена проверка на размер остатка, если он меньше самой маленькой панели, то добавляется одна маленькая панель.

int[] ExteriorWallSizesDraft(float remainder, int[] draft = null, int startIndex = 0)
{
    if (draft == null)
    {
        draft = new int[panels.Length];
        for (int i = 0; i < draft.Length; i++)
        {
            draft[i] = 0;
        }
    }
    if (remainder < panels[panels.Length - 1])
    {
        draft[draft.Length - 1] = 1;
        return draft;
    }
    for (var i = startIndex; i < panels.Length; i++)
    {
        draft[i] += (int)(remainder / panels[i]);
        remainder %= panels[i];
    }
    if (remainder > 0)
    {
        for (var i = 0; i < draft.Length; i++)
        {
            if (draft[i] != 0)
            {
                if (i == draft.Length - 1)
                {
                    return draft;
                }
                draft[i]--;
                remainder += panels[i];
                startIndex = i+1;
                break;
            }
        }
        draft = ExteriorWallSizesDraft(remainder, draft, startIndex);
    }
    return draft;
}

Сколько я ни силился, не смог понять принципа распределения панелей разной длины, поэтому после преобразования массива с количеством в массив с длинами, он перемешивается по алгоритму Фишера–Йетса. Получается вполне сносный результат.



▣ Проектируем фасад


На разных этажах хрущёвки используются разные панели. На первом этаже это окна и пустые стены, на втором могут встретиться балконы. Дополнительно у здания есть цоколь и иногда чердак. Для создания карты фасада можно взять массив с длинами стен и количество этажей и сделать двумерный массив, в котором в массиве каждого этажа хранится тип панели. Затем по этому шаблону можно наделать подходящих панелей.

Для парадного фасада алгоритм следующий. Нулевой этаж заполняется цокольными панелями. Первый этаж целиком заполняется окнами. Второй этаж полностью копирует первый, за исключением того, что часть окон заменяется балконами. Третий этаж и выше просто копируют шаблон второго этажа до самой крыши. Если есть чердак, то чердачные панели добавляются сверху.

Если балконы распихивать совсем случайным образом, то получается некрасивая мешанина, поэтому лучше в отдельном цикле размещать балконы сразу с двух сторон фасада симметрично относительно центра. Аналогично с окнами на торцевых фасадах, там на первом этаже могут быть и пустые стены и окна. Чтобы всё смотрелось хорошо, окна должны быть симметричны. На втором этаже торца часть окон аналогично парадному фасаду заменяется на балконы, но только на центральных панелях, на крайних панелях балконы бывают чрезвычайно редко.

Финальный штрих — двери. Если поделить число панелей на число дверей плюс один, то можно получить отрезки, через которые нужно размещать двери, чтобы они располагались более-менее равномерно. На самом деле в реальных хрущёвках вход в здание располагается иначе, если судить по фотографиям и планам, но я пока не смог придумать ничего лучше. На цокольном этаже — парадный вход, на последующих — окна, которые нужно на этапе создания модели немного смещать. На последнем этаже вместо окна ставится пустая низкая панель. В некоторых зданиях парадный вход бывает с крыльцом, тогда схема меняется, но мне было уже лень делать модели, поэтому такой вариант у меня не учитывается.

Код генератора фасадов
Для указания типа панели я использую перечисление, выглядит оно следующим образом:

public enum PanelType
{
    Wall,
    Window,
    Balcony,
    Entrance,
    EntranceWall,
    EntranceWallLast,
    Socle,
    Attic,
};


Ветвистый код ниже достаточно прост, имена переменных говорят за себя, объяснять особо нечего.

List<List<PanelType>> FacadePattern(int panelCount, int floorCount, bool haveAttic=false, bool longFacade=false, int entrancesCount=0)
{
    var panelPattern = new List<List<PanelType>>();
    var entranceIndex = panelCount / (entrances + 1);
    var entranceCount = 1;

    for (var i = 0; i < floorCount+1; i++)
    {
        panelPattern.Add(new List<PanelType>());
        for (var j = 0; j < panelCount; j++)
        {
            if (i == 0)
            {
                if (entrancesCount > 0 && j == entranceIndex && entranceCount <= entrances)
                {
                    panelPattern[0].Add(PanelType.Entrance);
                    entranceCount++;
                    entranceIndex = panelCount*entranceCount/(entrances + 1);
                }
                else
                {
                    panelPattern[0].Add(PanelType.Socle);
                }
            }
            else if (i == 1)
            {
                if (panelPattern[0][j] == PanelType.Entrance)
                {
                    panelPattern[1].Add(PanelType.EntranceWall);
                }
                else if (longFacade)
                {
                    panelPattern[1].Add(PanelType.Window);
                }
                else
                {
                    panelPattern[1].Add(PanelType.Wall);
                }
            }
            else
            {
                panelPattern[i].Add(panelPattern[i - 1][j]);
            }
            if (i == floorCount)
            {
                if (panelPattern[i - 1][j] == PanelType.Entrance || panelPattern[i - 1][j] == PanelType.EntranceWall)
                {
                    panelPattern[i][j] = PanelType.EntranceWallLast;
                }
            }
        }
        if (i == 1 && !longFacade)
        {
            for (int j = 0; j <= panelPattern[1].Count / 2; j++)
            {
                if (j != 0 && j != panelCount - 1 && Random.value > 0.5f)
                {
                    panelPattern[1][j] = PanelType.Window;
                    panelPattern[1][panelPattern[1].Count - 1 - j] = PanelType.Window;
                }
            }
        }
        if (i == 2)
        {
            for (int j = 0; j <= panelPattern[2].Count/2; j++)
            {
                if (panelPattern[2][j] == PanelType.Window && panelPattern[2][panelPattern[2].Count - 1 - j] == PanelType.Window && Random.value > 0.5f)
                {
                    panelPattern[2][j] = PanelType.Balcony;
                    panelPattern[2][panelPattern[2].Count - 1 - j] = PanelType.Balcony;
                }
            }
        }
    }
    if (haveAttic)
    {
        panelPattern.Add(new List<PanelType>());
        for (var j = 0; j < panelCount; j++)
        {
            panelPattern[panelPattern.Count-1].Add(PanelType.Attic);
        }
    }
    return panelPattern;
}




▣ Крышуем хрущёвку


Четыре фасада это ещё не дом, нужна крыша. Как выяснилось, крыши у хрущёвок бывают разные. Бывают плоские, бывают двускатные, бывают четырёхскатные. К сожалению, фотографий крыш хрущёвок в интернете ещё меньше, чем планов их этажей, поэтому крыши у меня получились голые. Вообще говоря, там должны быть вентиляционные колодцы и выходы на крышу, которые было бы просто разместить, если бы размеры здания были бы постоянными, но для моего случая нужно было придумывать алгоритм для размещения не очень понятно чего непонятно где. Так что пусть пока будут голые.

Для крыши я сделал модели с шириной и длиной один метр, а потом просто растягивал их как нужно.

Выбор крыши
switch (roofType)
{
    case RoofType.Flat:
        combine.Add(RandomItem(roofFlat));
        matrices.Add(Matrix4x4.TRS(roofHeight, Quaternion.identity, new Vector3(length, 1, width)));
        break;
    case RoofType.FlatOverhang:
        combine.Add(RandomItem(roofFlat));
        matrices.Add(Matrix4x4.TRS(roofHeight, Quaternion.identity, new Vector3(length + 1, 1, width + 1)));
        break;
    case RoofType.Gabled:
        combine.Add(RandomItem(roofGabled));
        matrices.Add(Matrix4x4.TRS(roofHeight, Quaternion.identity, new Vector3(length, 1, width + 1)));
        break;
    case RoofType.Hipped:
        combine.Add(RandomItem(roofHipped));
        matrices.Add(Matrix4x4.TRS(roofHeight, Quaternion.identity, new Vector3(length + 1, 1, width + 1)));
        break;
}

Вот и всё. Хрущёвка готова.



▣ Заключение


У описанного подхода есть одно узкое место. CombineMeshes мало того, что работает медленно, так у него ещё есть ограничение на количество вершин и треугольников для объединения. Если вы вдруг как я решили сделать хрущёвку-небоскрёб, то число вершин быстро превысит 65 тысяч, и получатся руины как на картинке ниже. Чтобы этого не случалось, нужно писать собственную функцию склейки моделей.



Исходники проекта и бинарники для разных платформ можете скачать по ссылкам ниже.

Внимание: Код по ссылкам ниже устарел, последнюю версию смотрите в Procedural Toolkit

Unity Web Player | Windows | Linux | Mac | Исходники на GitHub

Левая кнопка мыши — новое здание, Esc — Выход.

P. S. Буду рад, если кто-нибудь поможет с доработкой моделек, из меня моделлер плохой.
Даниил Басманов @BasmanovDaniil
карма
190,2
рейтинг 0,0
Геймдизайнер-телепат
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Спецпроект

Самое читаемое Разработка

Комментарии (89)

  • +25
    Что нам стоит дом построить? Нарисуем, будем жить!
  • +19
    Ожидал последним шагом «Хрущев-сити» :)
    • +103
      Вот, пожалуйста, наслаждайтесь :)

      • +40
        Добавьте сюда случайным образом расставленные спутниковые антенны, можно без алгоритма, просто по контуру дома, в случайных местах, чтоб реалистичнее, а также заполните все свободное пространство между домами стоящими машинами — и будет как фотография, неотличимо от реальности.
        • +39
          кондеи надо не забыть
          • –3
            Кондеи и хрущевки — вещь малосовместимая в реальности, вот антенны да. :)
            • +2
              Не согласен — уличную часть сплит системы довольно часто размещают на балконе или рядом с ним.
        • +15
          И еще надо делать рандомно застекленные балконы и рандомные окна. У кого пластик, а у кого дерево рассохшееся и газеткой заклеено.
          Тогда совсем аутентично будет:)
          • +28
            И рандомно запихивать на балконы лыжи и банки с соленьями.
            • +4
              И зимнюю/летнюю резину.
              • +3
                И велосипеды!
            • +1
              И санки, лопаты, шкафы с хламом

              И развешивать бельё
          • +3
            И машинами, машинами всё свободное пространство заполнить.
        • +1
          На незастекленных балконах можно добавить натянутые веревки с висящими на них семейниками :)
        • +4
        • 0
          Еще можно генератор микрорайонов.
      • +25
        «Архитектор борется с депрессией при помощи ЛСД»
      • +13
        В реальной жизни (Самара, Крутые Ключи):

        • +1
          ух ты, родной Кошелев)
          • 0
            Это возле Меги уже столько понастроили?
        • +8
          Такие города у меня всегда в какой-нибудь simcity выходили. И мне всегда было стыдно за них(
          • +1
            На самом деле тут не все так плохо, как может казаться. Тихий уютный пригород, вся инфраструктура на месте, рядом самый большой в Самаре ТЦ. Везде чисто, в отличии от большей (подавляюще большей) части остального города. А на заднем фоне, кстати, видно дата-центр мегафона (серое здание).
          • 0
            Не вижу, что не так. Аккуратная (по крайней мере на фото) малоэтажная застройка, много свободного места, газончики зеленые, елочки по линеечке посажены…
            • 0
        • 0
          Пипец, как всё одинаково. Тут даже не пахнет рандомом.
        • +2
          Я сначала картинку увидел, а текст не заметил. Подумал: «Крутой рендер, только чуток экспозицию подстроить, чтоб справа не засвечивалось.»
      • 0
        Их бы поворачивать и объединять в какие-нибудь осмысленные кварталы. Например, хрущёвки, стоящие крестом, или хрушёвки стоящие квадратом. А уже из этих кварталов собирать город.
        • 0
          Хрущёвки в советских городах практически никогда не объединялись в кварталы (если только не строились на месте старой городской сетки). Застраивали микрорайонами где улицы параллельны и перпендикулярны сами себе. Хаотично, точечно, бессистемно.
  • +11
    Спасибо, вы сделали мой день.
  • +6
    Если вы вдруг как я решили сделать хрущёвку-небоскрёб
    Хорошо хоть до этого не догадались архитекторы того времени. С ужасом сижу представляю свой район.
    Вопрос, если можно: а чем вызвано ограничение в 65к вершин? Ограничение Unity? А, вижу-вижу, пропустил.
    • +39
      Получился бы Гонконг.

      image

      Есть подозрение, что тогда просто технологий не было для массового строительства типовых башен, так бы с радостью за счет высоты экономили бы.
      • +14
        Для строительства — более-менее были. Но вот инфраструктурные вопросы:
        — дом выше 7-10 этажей нельзя газифицировать, соответственно, нужны мощные электросети
        — опять же — для высотных домов нужно больше возни с водопроводом
        — много тонкостей с пожарной безопасностью

        Но самое неприятное — дом не получится «типовым»: насколько я понимаю, нельзя из одних и тех же блоков собирать 5-этажку и 30-этажку: либо есть риск того, что нижние этажи не выдержат нагрузки, либо их прочность будет избыточна в невысоких домах.
        • 0
          Интересно, чем вызвано ограничение по высоте на газификацию?
          Сам жил на 19 этаже, сейчас в том же доме на 13-м. Газ есть. Страна, правда, другая и дом 2000-го года. Но тем не менее…
          • 0
            Думаю, по причине того, что газ тяжёлый, нужно увеличивать давление, чтобы продавить на верхние этажи

            Вряд ли ставят рекомпрессоры, коллекторы или ещё что-то, что накапливает газ и передаёт выше. (за названия не ручаюсь)

            Ниже отметили, что наоборот, давление делают высокое, а на нижних этажах ставят редукторы, уменьшающие давление
        • 0
          — дом выше 7-10 этажей нельзя газифицировать

          Живу в 15-этажной Питерской «свечке», на кухне газ.
          Правда, что выше 7-го, я не знаю. %)
          • 0
            На верхних этажах можно встретить электроплиты.
          • 0
            Тут вопрос в том, когда эта «свечка» была построена. ЕМНИП, дома выше 10 этажей запретили газифицировать после того, как в 1967 году в Москве был взрыв газа в жилом доме, в СНиП-ах 80-х годов точно есть норма об установке электроплит в домах этажностью 11 и выше. Кроме этого были, емнип, причины, связанные с давлением газа в высотных домах (и необходимостью устанавливать редукторы высокого давления) и «обратной тягой» в вентканалах, которая, якобы, могла задувать пламя на конфорках.

            Опять же — не скажу точно, но где-то читал, что якобы для Москвы пробили изменения в СНиП-ах, позволяющие ставить крышные котельные на газу и газифицировать дома произвольной этажности. Но так ли это и каков порядок такого фокуса — не скажу.
        • НЛО прилетело и опубликовало эту надпись здесь
      • +1
        Там одна из проблем была — недостаток строительной техники для построения именно высотных домов. Со временем-то эту проблему решили. Но пока решали — успели понатыкать 4-5-этажных хрущёвок.
      • +1
        Все просто: 5 этажей можно строить с минимальным фундаментом и без опорных балок + ленточный фундамент, все что выше — нужно лить монолитно-бетонные балки и делать нормальный фундамент (собственно это 16-этажки). Выше уже неоднородность каркаса, ветровые нагрузки и тд, то есть проект каждый нужно готовить и вносить изменения, поэтому и не заморачивались.

        У нас тут сносили пару пяти-этажек — они были заглублены на 40 см в землю, то есть там даже подвала не было — их разобрали, сыпанули несколько камазов земли и все — газон готов.
    • +5
      Подозреваю, что ограничение в 2-х байтном индексе вершин (0-65535). Тогда собственная функция склейки не поможет.
    • +3
      Додумались конечно. Были аналогичные (или несколько улучшенные) панельные 9- 12- и 16- этажные дома. Википедия
  • +7
    После прочтения статьи захотелось сделать обучалку по процедурной генерации чего-либо в Houdini.

    Нашел интро с генерацией моста (начинать смотреть с 3:15)
  • +7
    Осталось совсем немного и будет что-то вроде: www.youtube.com/watch?feature=player_embedded&v=V04dswEIcQU
    Да и вообще вот у него полно интересного на подобную тему: procworld.blogspot.ru/search/label/L-System

  • +3
    Можно еще добавить алгоритм для капитальных балконов тройной ширины на первом этаже
  • +8
    Сколько я ни силился, не смог понять принципа распределения панелей разной длины

    Генерируйте не сразу всю стену, а отдельно подъезды (для СПб — парадные :-) ).
    Каждый подъезд — строго прямоугольный, т.е. сумма длин блоков переднего и заднего фасадов подъезда должны совпадать.
    Слева и справа от двери короткая панель — там, как правило, расположена кухня.
    Балконы, как правило, делают смежные, т.е. варианта «без балкона-с балконом-без балкона» в живую, скорее всего, не встретите, только вариант «без балкона-с балконом-с балконом».
    • +1
      Опечатка, имелось в виду, варианта «с балконом — без балкона — с балконом» не встретите, только вариант «без балкона — с балконом — с балконом».
    • +8
      Хмм, а ведь правда, есть зависимость от расположения кухонь, сразу не заметил. Наверное у меня уже передозировка хрущёвками.



      Правда бывают еще и планы как на картинке ниже. Либо там внутренние стены кривые, либо расположение панелей и вправду отличается.

  • +2
    Вау! Это же круто!
    Спасибо, you made my day.
  • +3
    Мне какраз такого в Minecraft очень не хватает!
  • +3
    Вау! Слушайте, а спишитесь с мужиками из F4 Map, пусть у себя сделают такое!
  • +3
    Супер! Глянул еще ваш Royal Defenestrator. Это гениально) Кошечку не тронул, выкинул королеву.
  • +3
    Наверное, следующим шагом будет распечатка этих моделей на 3D принтере.
    А что, было бы круто!
    • +1
      в масштабе 1:1, чтобы сразу решить жилищный вопрос миллионов. :)
  • +1
    На крыше, как минимум можно генерить лифтовые шахты для домов с количеством этажей > 5.
    • 0
      Надо минимум для этажей хотя бы 3 поставить :)
      • +1
        nope

  • +2
    Прекрасная идея! Великолепно!
    Надо бы кое что добавить: не только три окна между подъездами, встречаются дома и по 4 окна, с балконными рядами в центре.
    Вы прям «головняк» с простыми зданиями в играх сняли :)
    А как это можно добавить в юнити тем людям, кто не умеет программировать?
    И как обозначить ваше авторство?

    Я хотел бы чтобы игроки сами добавляли такие здания в моей игре, а так же некоторые другие постройки и ваша идея ну просто в тему! никаких 3д редакторов не надо!
    • +2
      Встречаются и много-много окон подряд, а балконов нет. У меня тут рядом общага — там так и есть. А в посёлках есть панельные хрущёвки, которые вообще, судя по всему, собирались не по типовому проекту, а скорее всего вообще без проекта, то есть из того, что было — там и ассиметрия и всё, что угодно :)
      • 0
        асимметрия, простите…
        • 0
          да все в порядке — прекрасно вас понимаю так было в реальности, но вот только симметрия — более приятна глазу, а асимметрия — раздражает, и когда играешь долго становится заметно; поэтому смысл от асимметрии — сделать особенное здание, штучное-реалистичное; а от симметрии — типовое, распространенное; ну чтоб игроков не растерять.
    • 0
      Совсем без программирования пока что никак не получится, да и не стоит это добавлять в проект, там ещё много рефакторить нужно. Я планирую в будущем выпустить некий бесплатный набор инструментов для процедурной генерации, чтобы можно было просто бросить компонент в инспекторе и начать фигачить здания. Пока всё только ручками. Авторство можете не указывать, там пока ничего особо ценного нет, если очень хочется, можете написать «Даниил Басманов».

      Для интерактивной модификации здания нужно менять формат хранения данных о здании. Но вообще это не очень сложно. Можете написать мне в личку, если нужна доработка напильником под ваш проект.
      • 0
        Спасибо! Добавить авторство очень хочется. А если проект будет успешным, то хочется предложить вам даже долю в проекте, т.к. зданий там будет много самых разных, в т.ч. проихводственно-хозяйственного назначения.
        И спасибо за намерение помочь — я сначала сам по-пробую, а только потом смогу обратиться за помощью.
  • +1
    Неделя процедурных алгоритмов на хабре :) Коты, хрущевки, что следующее?
    ЗЫ просто обожаю разные процедурные алгоритмы
    • +3
      Процедурные ковры, люстры, игроки в домино во дворах и процедурный блатняк =)
  • +2
    А ещё про балконы. Если на 1 этаже магазин, не новодел из квартиры, а исконно советский магазин по проекту, то тогда на 2 этаже балконов нет, только с третьего начинаются. Первый этаж при этом обычно выше, но не всегда :)

    Но в целом автору уважение за генератор большей части моего города!
    • 0
      Вообще да — и магазин на первом этаже это тоже можно было бы добавить.

      А вы уже как-то добавили в юнити? я имею ввиду идею автора в вашу игру. Как вы это сделали? если не секрет.
      • 0
        Причём «не новодел» иногда может занимать первый этаж, выступать далеко вперёд (так что у второго этажа появляется что-то типа терассы) и иногда только с одного крыла.

        Дома с такими магазинами довольно редко встречаются, не в каждом доме. Я бы сказал, отношение один к восьми, а в некоторых кварталах вообще нет.
        • 0
          Тсс...! ;) у меня игра с магазинами будет…
  • +2
    Это шедевр постмодернизма! Нужно добавить процедурный генератор музыки (три аккорда, пара кастрюль, куски из Цоя и Высоцкого) — и будет прекрасная инсталяция =)
  • +3
    Вы бесплатно и очень быстро выдали результат визуально лучший, чем в проекте VC4, который собрал > $100K на кикстартере, и над которым трудилась куча народу на полном рабочем дне пару месяцев.
  • +12
    • 0
      Вот это «в свободное время поигрался», вот это я понимаю. Красиво, просто и «ятожетакмогунопочемутонедоходятрукиилипростоялентяй». Именно за счет последнего — молодцы такие энтузиасты! 5 баллов.
  • 0
    Собираетесь создать экономическую стратегию типа SimCity?
    • 0
      Нет, просто создаю инструменты процедурной генерации, чтобы потом их использовать в играх.
  • +6
    image
    а такого вида пристроечки будут?
    • +1
      Какой ужас!
    • +11
      А вот такие?

      Рекурсия балконов
      Recursion

      Итератор балконов
      Iterator

      «Особое» условие
      Magic if (...) condition

      Говнокод
      Kludges / duct tape
  • +2
    Осталось добавить учет розы ветров, ориентировки «Восток-Запад» и расчет воздушных потоков во всем микрорайоне.

    Пока мой мкр. пройдешь в метель — измотыляет во все стороны.
  • 0
    Даёшь генератор случайных городов для GTA )
  • +1
    У вас еще намного красочнее получилось чем в реальности. У тех домов что у нас в городе, количество цветов намного ограничено.
    • 0
      Один цвет? Серый, да?

      Иногда их всё-таки специально красят. И улицы после этого выглядят намного веселее.

      Но разброс цветов всё-таки не такой большой и цвета выбирают пастельные.
      • 0
        Иногда — это обычно перед приездом президента или какой-нибудь другой шишки.
        Ну, по крайней мере у нас это «иногда» случается именно в такие моменты :)
      • 0
        Со временем любая краска сходит, и остаются два цвета на основе красного кирпича и белого(который со временем превращается серый с коричневым оттенком).
        ИМХО, в хрущевках все же плюсов гораздо больше, и зря их так все не любят. Основную задачу они выполняют, это же не дорогое индивидуальное жильё.
  • +1
    Проект закрыт, а жаль: www.introversion.co.uk/subversion/

    • 0
      Оу… они его все-таки заморозили. Жаль, высокотехнологичная получалась игрушка.
      Хотя по скорости разработки было видно что до релиза долгий пусть.
    • +1
      Хнык. Ребят, это ведь прекрасно! Зачем было морозить проект? Есть ведь тот же кикстартер. Да это просто шедевр. Даже посидеть и от нечего делать погенерировать такие вот города.
  • +1
    Если к Генератору подключить 3D-принтер — проблема нехватки жилплощадей будет быстро решена… Останется только отделывать и обставлять, а для этого можно взять какой-нибудь 3D-конструктор интерьеров, тоже кстати на Unity :)
    А нас как раз есть такой 3D-конструктор (сегодняшняя статья). Кто третьим будет, с принтером кто?

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.