Сочленения
Итак, сочленения. По-буржуйски - джойнты (joints)
Сочленения - это объекты, которые могут определённым образом "сцеплять" тела. То есть например, в том же пресловутом Ragdoll'е все физ. тела для костей соединены между собой сочленениями. Если бы этого не было, что все части тела разлетелись бы кто куда и никаког орегдолла не получилось бы.
Джойнтов (во враппере, по крайней мере) существует достаточно много, рассмотреть их в одной статье не удастся, поэтому начну с самых основных.
Сферический джойнт.
Это самый распространённый вид сочленения. По сути, это шарнир, то есть имеет лимит в виде конуса. Чтобы лучше понять, как он будет работать, посмотрите на эту картинку:
Синий и красный цилиндры - это два тела, соединённые шарниром. Сферические части - это и есть собственно шарнир. Как видите, тела могут вращаться друг относительно друга только в пределах серого конуса.
Такой тип сочленений находит широкое применение. Например, с его помощью можно имитировать соединённые вместе звенья цепи, или сделать верёвки из тех же звеньев, или имитировать разные подвешенные предметы (болтающаяся под потолком лампочка) и т.д.
То есть применять можно везде где тела могут свободно вращаться.
Переходим к практике. Возьмём за основу Пример 2.
Будем создавать теперь уже не один кубик, а два вытянутых "кирпича", да ещё исоединённые между собой джойнтами.
Хочу сразу оговориться. Так как в каждом таком объекте уже будет 2 тела, а не одно, то пришлось бы создавать новые Field'ы для новых тел и мешей, менять обработку...
Но лучше поступим по-иному. Будем создавать сначала два pxCube'а, а потом между ними - джойнт.
Итак, напишем функцию, которая создаёт два куба типа pxCube, один чуть повыше другого. Понадобится также заставить функцию CreatepxCube возвращать созданный тип, чтобы мы могли к ним потом обратиться и создавать кубы более продолговатые:
Function CreateBunch(x#,y#,z#)
Cube1.pxCube = CreatepxCube(x#,y#+5,z#)
Cube2.pxCube = CreatepxCube(x#,y#-5,z#)
End Function
Function CreatepxCube.pxCube(x#,y#,z#)
pxC.pxCube = New pxCube
pxC\mesh = CreateCube()
ScaleEntity pxC\mesh,1,5,1
pxC\body = pxBodyCreateCube(1,5,1,1)
pxBodySetPosition pxC\body,x,y,z
Return pxC
End Function
CreateBunch создаёт 2 куба с разницей по высоте в 5. Теперь надо сцеплять их сферическим джойнтом. Делать это будем при помощи команды
pxJointCreateSpherical( body1%, body2%, x#, y#, z#, nx#, ny#, nz#)
|
Создаёт сферический джойнт (он же шарнир), возвращает его хендл.
Аргументы:
body1, body2 - тела, которые будем сцеплять.
x, y, z - координаты джойнта при его создании.
nx, ny, nz - нормализованный вектор направления оси джойнта. Попробую объяснить проще: помните серый конус с картинки выше? Эти параметры как раз указывают, куда будет направлен этот конус. Например, при 0,1,0 цилиндр будет направлен вверх.
Внимание! Тела body1, body2 должны быть динамическими (т.е. иметь массу <> 0). Если вы хотите привязать тело к статике, то подставьте 0 вместо статического тела.
Привяжем 2 наших кубика один к другому:
pxJointCreateSpherical Cube1\Body,Cube2\Body,x,y,z,0,1,0
Джойнт в данном случае нужно располагать посередине между двумя телами. Т.к. оба куба смещены по вертикали то соответственно и "конус" будет направлен вверх.
Теперь по пробелу уже создаём связку:
If KeyHit(57) Then CreateBunch(0,20,0)
|
Тыкаем на пробел, смотрим на результат.
Обратите внимание:
два сцеплённых друг с другом тела не коллизятся друг с другом!
Можно включить коллизию.
pxJointSphericalSetCollision(joint%)
|
Команда заставляет рассчитывать коллизию между двумя телами, сцеплёнными
сферическим джойнтом. (для других типов джойнтов - свои версии этой команды!) Аргументы: joint - хенджл джойнта.
Попробуем и в нашей проге это сделать. Так как нам теперь нужен хендл джойнта, то получим его в переменную и на него применим вышеописанную команду:
Joint = pxJointCreateSpherical (Cube1\Body,Cube2\Body,x,y,z,0,1,0)
pxJointSphericalSetCollision(Joint)
Ага, теперь они коллизятся. Только почти не поворачиваются и это понятно: т.к. куба сцеплены вплотную друг к другу, то они мешают друг другу вертеться. В функции создания поменяем расстояние между создаваемыми кубами. Вместо 5 возьмём 6.
Cube1.pxCube = CreatepxCube(x#,y#+6,z#)
Cube2.pxCube = CreatepxCube(x#,y#-6,z#)
Теперь всё хорошо видно. И вертятся и коллизятся. Но всё-таки ограничение по коллизии - это не вариант. Зачем точно рассчитывать коллизию хуллов, когда можно поступить гораздо проще - ограничить конус вращения?
Итак, уберём включение коллизии, вернём расстояние между кубами на 5 и рассмотрим, как можно по-иному ограничить вращение.
pxJointSphericalSetLimitAngle(joint%, angle#, hardn#, restit#)
|
Указывает джойнту лимит угла конуса вращения. Кстати, угол может принимать занчения и больше 90°, в реальном мире технически (при классическом шарнире) это было бы невозможно (шарнир бы просто вывалился) или потребовало бы каких-то иных путей решения задачи, но так как у нас тут компьютерное моделирование физики, то мы можем сотворить всё что угодно.
Аргументы:
joint - хендл джойнта
angle - угол лимита в градусах
hardn - жёсткость лимита. Диапазон: [0;1] По умолчанию: 1.
restit - упругость лимита. Диапазон: [0;1] По умочанию: 0.
*Очень странно, но при любых значениях hardn и restit получается один и тот же эффект.
При значениях за пределами указанного диапазона лимит отключается вовсе. Use it wisely.
Укажем лимит в 90°, остальные параметры оставим как по дефолту и посмотрим, что выйдет:
pxJointSphericalSetLimitAngle(Joint, 90,1, 0)
Вот, теперь тела не могут вращаться более чем на 90° от изначального положения.
*Это важно: в школьной геометрии (11-й класс) углом конуса считается угол при вершине его осевого сечения, что логически верно. Но в PhysX'е указывается только половина этого угла. Взгляните на картинку: угол, задавемый параметром angle вышеописанной функции обозначен синим:
Будьте бдительны и не ведитесь на дезинформацию минобразования РФ =-)))
Тела крутятся вокруг своей оси. А что, если это например нога человека? Ведь она же не может крутиться на 360° ? Это дело тоже можно ограничить.
pxJointSphericalSetLimitTwist(joint%,mintwist#,max twist#,spr#,damp#,targetVal#)
|
Ограничивает кручение вокруг оси джойнта. Аргументы:
joint - джойнт
mintwist,maxtwist - минимально и максимально возможное кручение
spr - упругость. Диапазон: [0;бесконечность), по умолчанию: 0
damp - "вязкость"
targetVal - значение, к которому будет стремиться поворот.
Создадим ограничение +- 10° с некоторой упругостью и вязкость. Целевое значение пусть будет 0
pxJointSphericalSetLimitTwist(Joint,-10,10,10,1,0)
Вот, мы ограничили вращение в разумных пределах.
Рассмотрим ещё одну команду для сферического джойнта:
pxJointSphericalSetLimitSpring(joint%, spr#, damp#, targetVal#)
|
Указывает растяжимость соединения. Аргументы:
joint - джойнт
spr - эластичность. По дефолту 0, может принимать значения от 0 до бесконечности.
damp - вязкость
targetVal - значение к которому стремиться. По дефолту 0.
Установим эту самую растяжимость:
pxJointSphericalSetLimitSpring(Joint, 10, 1, 0)
И последнее. Чтобы удалить джойнт используйте
Тупо удаляет джойнт.
Аргументы: joint - джойнт
Чтобы на практике заюзать это, нужно где-то хранить хендлы всех созданных джойнтов. Для этого я создал соответствующий тип, функцию удаления всех джойнтов и при создании новой пары кубов создаю новый тип и в него заношу хендл нового джойнта:
Type Joint
Field Joint
End Type
Function FreeAllJoints()
For j.Joint = Each Joint
pxDeleteJoint(J\Joint)
Delete J
Next
End Function
.......
Function CreateBunch(x#,y#,z#)
Cube1.pxCube = CreatepxCube(x#,y#+5,z#)
Cube2.pxCube = CreatepxCube(x#,y#-5,z#)
Joint = pxJointCreateSpherical (Cube1\Body,Cube2\Body,x,y,z,0,1,0)
pxJointSphericalSetLimitAngle(Joint, 30,1, 0)
pxJointSphericalSetLimitTwist(Joint,-10,10,10,1,0)
pxJointSphericalSetLimitSpring(Joint, 10, 1, 0)
J.Joint = New Joint
J\Joint = Joint
End Function
Вызов по левому шифту:
If KeyHit(42) Then FreeAllJoints()
Теперь если нажать шифт, то все кубики разваливаются)
Кстати, обратите внимание на то, что при уничтожении джойнта коллизия между двумя телами снова включается и они "выскакивают" друг из друга. Поэтому если вы собрались в процессе игры разрушать некоторые джойнты, то коллизию всё-таки лучше включить.
Полный код примера вы найдёте в аттаче "PhysXExample13.zip"
В этот раз хотел написать ещё и про хиндж, да не успеваю в СЗИП СПБГУТД на этой неделе будет тестирование, по итогам которого соответственно повышаются шансы на поступление, так что надо готовиться. В-общем, кто учился в 11-м классе - тот поймёт. Не могу сказать точно, будет ли следующий пост через неделю. Если нет - напишу позже. На сегодня у меня всё