Программная генерация Mesh в Unity

Программная генерация Mesh в Unity

Что ни говори, а все-таки приятно в Unity программировать. Документация самая полная и хорошо структурирована. Практически на каждый метод и свойство класса есть свой небольшой пример. И даже если вы задумали генерировать модели объектов программно (т.е. работать напрямую с объектом Mesh и его вершинами), то и здесь можно разобраться самому при помощи одной только документации. Помнится, первый опыт программной работы с Mesh в Unity у меня был, когда я разбирался с созданием разрушаемого окружения в Unity. Тогда, правда, сам код я так и не дописал.

Но вот теперь снова столкнулся с необходимостью программной генерации Mesh в Unity. На сей раз мне это понадобилось для отрисовки отладочных объектов (кубиков). При помощи этих кубиков я разбиваю игровое пространство на кластеры, создавая таким способом BSP-дерево. Что получается, видно на рисунке в начале статьи. А сам код идет далее…


private void DrawBox(CSPTreeCell cell, string namePrefix = "")
{
        // Программно создаем объект в Unity
        // Создаем кубик, но это не важно, т.к. все вершины мы создадим заново
	GameObject go = GameObject.CreatePrimitive(PrimitiveType.Cube);

        // Назовем новый объект, как душе угодно
	go.name = namePrefix+cell.Index;

        // Помещаем его куда надо
	go.transform.position = cell.Location;

        // А тут удаляем компонент для расчета коллизий,
        // иначе главный герой будет биться об отладочный кубик,
        // вместо того, чтобы проходить сквозь него
	Destroy(go.GetComponent<Collider>());
	
        // Теперь нам надо добраться до MeshRenderer
	MeshRenderer mr = go.GetComponent<MeshRenderer>();

        // и назначить для нашего отладочного кубика нужный материал
	mr.material = new Material(DebugMaterial);
		
	// И, наконец, получаем доступ к Mesh'у Unity
	Mesh m = go.GetComponent<MeshFilter>().mesh;
	
        // Полностью очищаем Mesh, удаляем все предыдущие вершины
	m.Clear();

        // Создаем массивы, в которых будут располагаться наши новые вершины,
        // треугольники и UV-координаты
	Vector3[] vertices = new Vector3[30];
	int[] triangles    = new int[30];
	Vector2[] uv       = new Vector2[30];

        // На самом деле векторов должно быть 36, но я решил не отрисовывать
        // одну из плоскостей кубика, потому что все нужные мне ребра куба
        // видны и без крышки. Если надо, допишите код самостоятельно :)

        // Тут я поочередно создаю нужные мне плоскости куба.
        // Как видно из кода, я отрисовываю в виде кубика ячейку BSP-дерева.
        // Вся "магия" происходит в методе CreatePlane. См. ниже :)
        CreatePlane(0, ref vertices, ref triangles, ref uv, new Vector3[4] {
			Vector3.zero,
			new Vector3(cell.Box.x, 0f, 0f),
			new Vector3(cell.Box.x, cell.Box.y, 0f),
			new Vector3(0f, cell.Box.y, 0f)}
	);
		
	CreatePlane(6, ref vertices, ref triangles, ref uv, new Vector3[4] {
			Vector3.zero,
			new Vector3(0f, 0f, cell.Box.z),
			new Vector3(0f, cell.Box.y, cell.Box.z),
			new Vector3(0f, cell.Box.y, 0f)}
	);
		
	CreatePlane(12, ref vertices, ref triangles, ref uv, new Vector3[4] {
			Vector3.zero,
			new Vector3(cell.Box.x, 0f, 0f),
			new Vector3(cell.Box.x, 0f, cell.Box.z),
			new Vector3(0f, 0f, cell.Box.z)}
	);
		
	CreatePlane(18, ref vertices, ref triangles, ref uv, new Vector3[4] {
			new Vector3(0f, cell.Box.y, 0f),
			new Vector3(cell.Box.x, cell.Box.y, 0f),
			new Vector3(cell.Box.x, cell.Box.y, cell.Box.z),
			new Vector3(0f, cell.Box.y, cell.Box.z)}
	);
		
	CreatePlane(24, ref vertices, ref triangles, ref uv, new Vector3[4] {
			new Vector3(cell.Box.x, 0f, 0f),
			new Vector3(cell.Box.x, 0f, cell.Box.z),
			new Vector3(cell.Box.x, cell.Box.y, cell.Box.z),
			new Vector3(cell.Box.x, cell.Box.y, 0f)}
	);

        // Все! Массивы векторов готовы, можем записать их обратно в Mesh
        m.vertices = vertices;
	m.uv = uv;
	m.triangles= triangles;
}

/**
 * Отрисовка плоскости, заданной четырьмя точками по часовой или против часовой стрелки
 */
private void CreatePlane(int startIndex, ref Vector3[] vertices, ref int[] triangles, ref Vector2[] uv, Vector3[] points)
{
        // Как мы знаем, плоскость состоит из двух треугольников:

	// - Первый треугольник
	vertices[startIndex + 0] = points[0];
	vertices[startIndex + 1] = points[1];
	vertices[startIndex + 2] = points[2];
	triangles[startIndex + 0] = startIndex +0;
	triangles[startIndex + 1] = startIndex +1;
	triangles[startIndex + 2] = startIndex +2;
	uv[startIndex + 0] = new Vector2(0,0);
	uv[startIndex + 1] = new Vector2(0,1);
	uv[startIndex + 2] = new Vector2(1,1);
	
		
	// - Второй треугольник
	vertices[startIndex + 3] = points[0];
	vertices[startIndex + 4] = points[2];
	vertices[startIndex + 5] = points[3];
	triangles[startIndex + 3] = startIndex +3;
	triangles[startIndex + 4] = startIndex +4;
	triangles[startIndex + 5] = startIndex +5;
	uv[startIndex + 3] = new Vector2(0,0);
	uv[startIndex + 4] = new Vector2(1,1);
	uv[startIndex + 5] = new Vector2(1,0);
}


Немного поясню назначение массивов вершин и треугольников. С массивом вершин, кажется, все ясно — это точки в пространстве, в которых располагаются наши вершины. А что из себя представляет массив треугольников? Это ни что иное, как ссылки на индексы вершин, которые будут образовывать треугольник. Эти ссылки идут пачками по три элемента. Важно понимать, что треугольники могут образовывать любые вершины. Т.е. одна и та же вершина может присутствовать сразу в нескольких треугольниках.

В моем примере я решил задачу, что называется, «в лоб», т.е. насоздавал отдельных вершин под каждый треугольник. Разумнее было использовать одну и туже вершину для смежных треугольников, но зато у меня получился универсальный метод для отрисовки плоскостей :)
  • 0
  • 15 декабря 2012, 16:11
  • dimanjy

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

RSS свернуть / развернуть

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