Показать сообщение отдельно
Старый 06.01.2009, 22:33   #4
ABTOMAT
Ференька
 
Аватар для ABTOMAT
 
Регистрация: 26.01.2007
Адрес: улица Пушкина дом Колотушкина
Сообщений: 10,741
Написано 5,461 полезных сообщений
(для 15,675 пользователей)
Ответ: Учебник по PhysX Wrapper для Blitz3D

Хулл, или как сделать физическую модель в 3D Studio max'е

В прошлые разы я рассказал об основных физических примитивах на PhysX Wrapper'е, однако только ими дело не ограничивается. Можно (куда ж без этого?) создавать физические тела произвольной формы. Для этого существуют Hull, Trimesh и Compound.

Итак, начнём с Хулла. Хулл - это выпуклый физический объект. Обратите внимание: выпуклый.

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

Если это всё-таки возможно, то такое тело выпуклым не будет:

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

Чем-то напоминает пресс-папье. Как видите, эта модель выпуклая.

Теперь давайте используем её в Блитзе. Возьмём за основу Пример 3 (ищите в аттачах выше). В ём мы могли создавать различные физические объекты по нажатию пробела. Но давайте-ка их выкинем от греха подальше и заменим их на вышеуказанный объект. Я сохранил ту модель в "PressPapier.b3d" и теперь код создания физ. объекта будет выглядеть примерно так (в самом начале проги я загрузил модель в глобальную переменную PressPapierMesh, при создании только копирую её):
Global PressPapierMesh = LoadMesh("PressPapier.b3d")
HideEntity PressPapierMesh
...
Function CreatepxBody(x#,y#,z#)    
    pxB.pxBody = New pxBody
    
    pxB\mesh = CreateCube()
    pxB\body = pxBodyCreateCube(1,1,1,1)

    pxBodySetPosition pxB\body,x,y,z
End Function
Так, вроде, работает. Но физической моделью всё ещё остаётся кубик. Надо заменить его на физ. тело, соответствующее нашей модели. Нам надо познакомиться с командой создания хулла:
pxBodyCreateHull%( vbank%, nvert%,mass#)
vbank - ссылка на банк, в котором содержится информация о вершинах. Структуру банка я опишу ниже.
nvert - количество вершин, из которых будет состоять хулл.
mass - масса
Количество треугольников, которое получится в конечном хулле не должно превышать 512 ! Так что не надо пытаться строить хуллы из High-Poly моделей.

Итак, самая главная заноза в заднице при создании хулла - это банк вершин. Если вы не умеете работать с банками в Блитзе - лучше почитайте о них, потому без базовых навыков работы с ними придётся туговато.

Структура банка вот такая:
Размер банка - количество вершин * 12

n*12+0 - координата X n-ной вершины (вещественное число - float)
n*12+4 - координата Y n-ной вершины (вещественное число - float)
n*12+8 - координата Z n-ной вершины (вещественное число - float)
Кстати, в хелпе этого нет Досадное упущение.

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

Function BodyCreateHull%(mesh%, mass#)

    Local nsurf = CountSurfaces(mesh)
    Local nvert = 0
    For ns = 1 To nsurf
        Local surf = GetSurface(mesh,ns)
        nvert = nvert + CountVertices(surf)
    Next
         vbank = CreateBank(nvert*4*3)
    nv = 0
    For ns = 1 To nsurf
        surf = GetSurface(mesh,ns)
        nvv = CountVertices(surf)
        For nvc = 0 To nvv - 1
             PokeFloat vbank,nv*12+0,VertexX(surf,nvc)
             PokeFloat vbank,nv*12+4,VertexY(surf,nvc)
             PokeFloat vbank,nv*12+8,VertexZ(surf,nvc)
            nv = nv+1
        Next
    Next
    Local bbb%= pxBodyCreateHull(vbank, nvert, mass)
    FreeBank vbank
    Return bbb
End Function
Как видите, в эту функцию нужно только передать зендл меша и массу. Всю остальную работу она выполнит сама и возвратит хендл физического объекта - то, что нам нужно.

Но тут есть одна неприятность. Дело в том, что если использовать (в Максе или в Блитзе) различные трансформации вроде поворотов и масштабов, то координаты вершин непосредственно не затрагиваются, а меняются некие параметры объекта, на основе которых перед рендером он будет отскейлен и повёрнут как надо. Но ведь мы считываем информацию непосредственно с вершин модели! А это означает, что вышеописанные трансформации никакого эффекта не дадут. Очень часто на форумах пишут о том, что тело из модели создаётся неправильно и т.п. - в 90% случаев причиной как раз является то, что я только что описал. Как же быть?
  • Перед экспортом из Макса следует произвести такую операцию: выделите объект, перейдите на вкладку "Utilities" справа и нажмите на кнопку "ResetXForm" а затем на "Reset Selected" (см. картинку). Также лучше отключите все материалы и группы сглаживания.

    В других редакторах не знаю как - ищите сами либо спрашивайте на соотв. форумах. Буду рад добавить если кто подскажет.
  • В блитзе с объектом, из которого будете делать Hull, НЕ использйуте команды: TurnEntity, RotateEntity, MoveEntity, PositionEntity, ScaleEntity. Вместо них надо использовать PositionMesh, RotateMesh, ScaleMesh.
Хочется добавить, что если вы попытаетесь создать хулл из невыпуклого меша, то при создании "вмятины" будут "заровнены", т.е. ошибок не вылетит и тело создастся. Но всё же лучше следите за формой мешей сами. Ведь очень неприятно получить не то, что ты делал.

В моей модели я уже всё сделал по-вышеописанному, так что проблем быть не должно. Однако помните об этом, это очень важно.
Так-с, теперь наконец можно непосредственно создать хулл. Пусть у него будет масса 1 (о да, я знаю, что у меня богатая фантазия на выбор массы ), тогда код создания будет выглядеть так:
BodyCreateHull(PressPapierMesh, 1)
Вставим это вместо pxBodyCreateCube в нашей функции. Теперь запускаем и любуемся. Вроде, поведение объектов соответствует визуальной модели и это хорошо.

Но мы каждый раз при создании физ. объекта генерируем хулл из вершин. Это отнимает немного времени, но всё же оно достаточно значительное: до 1 мс. Но можно очень легко избавиться от этого: в самом начале создадим хулл, а потом будем только копировать его. Копирование займёт гораздо меньше времени, чем создание. Итак, в самом начале рядом с загрузкой модели создадим ещё одну глобальную переменную, в которую мы создадим хулл и откуда будем копировать тело:
Global PressPapierHull = BodyCreateHull(PressPapierMesh, 1)
Так, теперь настало время копировать физическое тело. Тут нам поможет команда
pxCopyBody%(body%)
Эта команда копирует тело. Она практически идентична блитзевскому CopyEntity, так что, я думаю, с пониманием принципа её использования у вас не возникнет проблем. Помните, что копия тела создаётся в точке 0,0,0 с поворотом 0,0,0. Почему-то многие забывают об этом, хотя это и очевидно. Помните, что копирование происходит намного быстрее, чем создание. Поэтому использовать нужно в первую очередь копирование, если это возможно.

Итаке, вместо того, чтобы заново рассчитывать тело при создании очередной "промокашки":
pxB\body = BodyCreateHull(PressPapierMesh, 1)
мы будем его просто копировать:
pxB\body = pxCopyBody(PressPapierHull)
Запускаем, смотрим. Вот и первая проблема. Похоже, что на полу чаляется какое-то невидимое тело. Это, как нетрудно догадаться, то тело, которое мы сгенерировали в самом начале чтобы делать из него копии. Вот мы его создали и оно валяется бесхозное, мозоля глаза. Надо что-то делать.

А делать мы будем вот что: отключим телу коллизию с другими телами, дабы оно не влияло на их перемещение. Делается это при помощи команды
xBodySetFlagCollision%(body%,stat%)
Устанавливает флаг коллизии с телом.
stat - показывает, нужно ли включить или отключить коллизию. 1 - вкл, 0 - выкл.
body - тело, к которому этот флаг применяется.

В нашем коде отключение коллизии для PressPapierHull будет выглядеть примерно так:

pxBodySetFlagCollision(Int PressPapierHull, 0)
Ну вот, теперь нам ничего не мешает там, внизу.

Полный код примера вы найдёте в аттаче "PhysXExample5.zip"

Но теперь нужно рассказать ещё об одной важной вещи.
Затраты на обработку физических тел всё-таки ощутимо больше, чем на обработку моделей, состоящих из такого же количества вершин и граней. Поэтому нелишним будет упрощать физические модели насколько это возможно. Посмотрим на нашу "промокашку". В её модели 76 треугольников и она кажется вполне округлой. Я намеренно сделал такую высокую сегментацию, чтобы вы могли оценить разницу. Соответственно, и Hull, созданный из неё тоже будет состоять из эквивалентного числа граней. Но это явно избыточно. Думаю, если она будет немного более угловатой, игрок ничего не заметит. Кроме того, при экспорте модели для последующего создания из неё хулла нужно следовать определённым правилам (ищите выше), что не всегда удобно при моделировании. Лучше физическую модель делать отдельно.

Кстати! (Это очень важно!) У визуальной модели и у физической модели центры должны находиться в одних и тех же координатах! Для удобства лучше делать их в одном и том же max-файле, чтобы физ. модель совпадала с визуальной насколько это возможно.
Вот, сделал аналогичную модель в Максе, она состоит из меньшего числа полигонов:

Её я сохранил в файл "PressPapierPhys.b3d"
Кстати! Т.к. нам нужны лишь координаты вершин, то такую информацию, как нормали, текстуры, материалы и т.п. можно (и нужно) не сохранять!
Вот теперь загрузим модельку из этого файла, создадим хулл уже из неё и саму модель удалим, т.к. она нам больше не понадобится (если вы внимательно читали всё что я писал ранее, то код должен быть понятен):
HullMesh = LoadMesh("PressPapierPhys.b3d")
Global PressPapierHull = BodyCreateHull(HullMesh, 1)
pxBodySetFlagCollision(Int PressPapierHull, 0)
FreeEntity HullMesh
Запускаем, смотрим. Ха! На глаз практически не отличишь, а ресурсов кушает теперь заметно меньше!



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

Полный код примера вы найдёте в аттаче "PhysXExample6.zip"

В другой раз я расскажу о тримешах и компаундах. Хотел описать их в этом посте, но тема вышла уж очень обширной. Напомнию, все вопросы и предложения можно запостить здесь.
Вложения
Тип файла: zip PhysXExample5.zip (2.5 Кб, 1949 просмотров)
Тип файла: zip PhysXExample6.zip (3.1 Кб, 1896 просмотров)
__________________
Мои проекты:
Анальное Рабство
Зелёный Слоник
Дмитрий Маслов*
Различие**
Клюква**

* — в стадии разработки
** — в стадии проектирования
Для проектов в стадии проектирования приведены кодовые имена


Последний раз редактировалось ABTOMAT, 07.01.2009 в 01:43.
(Offline)
 
Ответить с цитированием
Эти 44 пользователя(ей) сказали Спасибо ABTOMAT за это полезное сообщение:
3dr1aN (09.01.2009), ACTIVATOR (20.01.2009), Alex_Noc (09.01.2009), Andvrok (21.10.2009), Arles (25.08.2009), baton4ik (01.02.2010), Blender (17.01.2010), Brain (15.01.2010), CRASHER (18.01.2009), den (27.07.2010), Diablomania (14.08.2009), Dream (09.01.2009), Eugenes (01.07.2012), EvilChaotic (28.08.2009), h1dd3n (31.01.2009), H@NON (09.01.2009), Harter (07.01.2009), HolyDel (17.01.2009), Hurrit (16.05.2010), Максим (09.01.2009), is.SarCasm (20.02.2010), johnk (07.01.2009), laaqiq (18.01.2010), le-den (09.01.2009), Main Cry (23.04.2009), Mind (03.04.2011), m_512 (13.02.2009), Nex (07.01.2009), NitE (07.01.2009), PackegerX (03.02.2010), radiantstudio (07.04.2011), Randomize (20.01.2009), SashaRX (09.01.2009), Sashka007 (07.01.2009), SBJoker (06.01.2009), Slavik (18.06.2009), strayhnd (30.06.2010), Tadeus (09.01.2009), tirarex (12.12.2011), tormoz (06.01.2009), viper86 (08.02.2009), WhiteBlack (27.07.2010), Zer0n (21.06.2010), ІГРОГРАЙКО (02.07.2009)