netlib.narod.ru | < Назад | Оглавление | Далее > |
Давайте попробуем изобразить на экране сценку из жизни леса: в ветвях весело щебечут птички, журчит ручеек, где-то ухает филин. Неплохая мысль — но для начала придется нарисовать что-нибудь, хотя бы отдаленно напоминающее дерево. На рис. 4.25 изображена елка (команда Edit | A Tree).
Рис. 4.25. Елка
Конечно, елки бывают и покрасивее, но пока сойдет и такая. Наше дерево состоит из 25 вершин и 19 граней. Если мы хотим создать целый лес из 100 деревьев (из остальных елок сделали бумагу для книги, которую вы читаете), можно включить в макет еще 99 деревьев, похожих на рис. 4.25. Самое время подумать о том, как приказать механизму визуализации нарисовать еще 99 деревьев, в точности аналогичных первому, но расположенных в других местах макета.
Помните, что мы говорили о фреймах и визуальных элементах в главе 3? Объект C3dShape содержит фрейм, определяющий его положение, размер, ориентацию и т.д., а также визуальный элемент, который по сути дела описывает набор вершин, граней и т.д. для объекта, который мы хотим увидеть на экране. Функция C3dShape::Clone позволяет включить один и тот же визуальный элемент в несколько разных фреймов. Она создает новый объект C3dShape по существующему объекту, но вместо того, чтобы строить для него новый визуальный элемент, она присоединяет к объекту визуальный элемент исходной фигуры. Таким образом, чтобы изобразить лес, можно создать одно дерево и продублировать его 99 раз. Результат изображен на рис. 4.26 (команда Edit | A Forest).
Рис. 4.26. Лес
Давайте рассмотрим функцию, в которой строится лес, поскольку некоторые аспекты процесса дублирования оказываются неочевидными.
void CMainFrame::OnEditForest() { NewScene(); // Создать первое дерево вместе со стволом m_pShape = new C3dShape; m_pShape->CreateCone(0, 0, 0, 1, TRUE, 0, 4, 0, 0, FALSE); C3dShape trunk; trunk.CreateRod(0, -2, 0, 0, 0, 0, 0.2); m_pShape->AddChild(&trunk); // Присоединить дерево к макету m_pScene->AddChild(m_pShape); // Получить позицию и ориентацию ствола // по отношению к родителю (дереву) C3dVector p, d, u; trunk.GetPosition(p); trunk.GetDirection(d, u); // Дублировать дерево 99 раз for (int i = 0; i < 99; i++) { // Дублировать крону и ствол C3dShape* pTree = m_pShape->Clone(); C3dShape* pTrunk = trunk.Clone(); pTree->AddChild(pTrunk); // Задать относительную позицию ствола // по отношению к кроне pTrunk->SetPosition(p); pTrunk->SetDirection(d, u); // Присоединить дубли как потомков первого дерева, // чтобы можно было вращать весь лес m_pShape->AddChild(pTree); // Задать положение нового дерева в макете pTree->SetPosition(((double)(rand() % 100) / 5) — 10.0, 0, (double)(rand() % 100) / 5, m_pScene); // Удалить контейнеры delete pTrunk; delete pTree; } }
Непременно обратите внимание на то, что ствол присоединяется в качестве потомка к конусу, образующему крону дерева, поэтому при дублировании кроны со стволом важно, чтобы при присоединении ствола-дубликата к кроне-дубликату была сохранена их относительная позиция. Если этого не сделать, ствол будет развернут в каком-нибудь странном направлении и по всей вероятности вообще не будет соединен с кроной. Попробуйте закомментировать фрагменты позиционирования (pTree->SetPosition) и ориентации ствола и заново постройте приложение, чтобы посмотреть, что из этого получится.
Второй важный момент заключается в том, что деревья-дубликаты становятся потомками самого первого дерева, чтобы можно было вращать весь лес с помощью одной переменной m_pShape (первое дерево). Поскольку дубликаты являются потомками первого дерева, их позиция должна быть задана по отношению ко всему макету. Если удалить из вызова pTree->SetPosition необязательный аргумент-фрейм (m_pScene), позиция будет указываться по отношению к родителю, и деревья расположатся неверно. Проверьте!
netlib.narod.ru | < Назад | Оглавление | Далее > |