TestScene.lua 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583
  1. ExecuteFile("LuaScripts/Utilities/Network.lua")
  2. local testScene
  3. local camera
  4. local cameraNode
  5. local yaw = 0
  6. local pitch = 0
  7. local drawDebug = 0
  8. local context = GetContext()
  9. local audio = GetAudio()
  10. local cache = GetCache()
  11. local engine = GetEngine()
  12. local fileSystem = GetFileSystem()
  13. local graphics = GetGraphics()
  14. local input = GetInput()
  15. local network = GetNetwork()
  16. local renderer = GetRenderer()
  17. local ui = GetUI()
  18. function Start()
  19. if not engine:IsHeadless() then
  20. InitConsole()
  21. InitUI()
  22. else
  23. OpenConsoleWindow()
  24. end
  25. ParseNetworkArguments()
  26. InitScene()
  27. SubscribeToEvent("Update", "HandleUpdate")
  28. SubscribeToEvent("KeyDown", "HandleKeyDown")
  29. SubscribeToEvent("MouseMove", "HandleMouseMove")
  30. SubscribeToEvent("MouseButtonDown", "HandleMouseButtonDown")
  31. SubscribeToEvent("MouseButtonUp", "HandleMouseButtonUp")
  32. SubscribeToEvent("PostRenderUpdate", "HandlePostRenderUpdate")
  33. SubscribeToEvent("SpawnBox", "HandleSpawnBox")
  34. SubscribeToEvent("PhysicsCollision", "HandlePhysicsCollision")
  35. network:RegisterRemoteEvent("SpawnBox")
  36. if runServer then
  37. network:StartServer(serverPort)
  38. SubscribeToEvent("ClientConnected", "HandleClientConnected")
  39. -- Disable physics interpolation to ensure clients get sent physically correct transforms
  40. testScene:GetPhysicsWorld():SetInterpolation(false)
  41. end
  42. if runClient then
  43. network:Connect(serverAddress, serverPort, testScene)
  44. end
  45. end
  46. function Stop()
  47. testScene = nil
  48. camera = nil
  49. cameraNode = nil
  50. end
  51. function InitConsole()
  52. local uiStyle = cache:GetXMLFile("UI/DefaultStyle.xml")
  53. local debugHud = engine:CreateDebugHud()
  54. debugHud.defaultStyle = uiStyle
  55. debugHud.mode = DEBUGHUD_SHOW_ALL
  56. local console = engine:CreateConsole()
  57. console.defaultStyle = uiStyle
  58. end
  59. function InitUI()
  60. local uiStyle = cache:GetXMLFile("UI/DefaultStyle.xml")
  61. local newCursor = Cursor:new(context)
  62. newCursor.styleAuto = uiStyle
  63. newCursor.position = IntVector2(graphics:GetWidth()/ 2, graphics:GetHeight() / 2)
  64. ui.cursor = newCursor
  65. if GetPlatform() == "Android" or GetPlatform() == "iOS" then
  66. ui.cursor.visible = false
  67. end
  68. end
  69. function InitScene()
  70. testScene = Scene(context)
  71. -- Create the camera outside the scene so it is unaffected by scene load/save
  72. cameraNode = Node(context)
  73. camera = cameraNode:CreateCamera()
  74. cameraNode.position = Vector3(0, 2, 0)
  75. if not engine:IsHeadless() then
  76. renderer:SetViewport(0, Viewport:new(context, testScene, camera))
  77. -- Add bloom & FXAA effects to the renderpath. Clone the default renderpath so that we don't affect it
  78. -- local newRenderPathPtr = renderer:GetViewport(0):GetRenderPath():Clone()
  79. -- local newRenderPath = newRenderPathPtr:Get()
  80. local newRenderPath = renderer:GetViewport(0):GetRenderPath():Clone()
  81. newRenderPath:Append(cache:GetXMLFile("PostProcess/Bloom.xml"))
  82. newRenderPath.Append(cache:GetXMLFile("PostProcess/EdgeFilter.xml"))
  83. newRenderPath:SetEnabled("Bloom", false)
  84. newRenderPath:SetEnabled("EdgeFilter", false)
  85. renderer:GetViewport(0):SetRenderPath(newRenderPath)
  86. audio:SetListener(cameraNode:CreateSoundListener())
  87. end
  88. if runClient then
  89. return
  90. end
  91. local world = testScene:CreatePhysicsWorld()
  92. testScene:CreateOctree()
  93. testScene:CreateDebugRenderer()
  94. local zoneNode = testScene:CreateChild("Zone")
  95. local zone = zoneNode:CreateZone()
  96. zone.ambientColor = Color(0.15, 0.15, 0.15)
  97. zone.fogColor = Color(0.5, 0.5, 0.7)
  98. zone.fogStart = 100.0
  99. zone.fogEnd = 300.0
  100. zone.boundingBox = BoundingBox(-1000, 1000)
  101. if true then
  102. local lightNode = testScene:CreateChild("GlobalLight")
  103. lightNode.direction = Vector3(0.3, -0.5, 0.425)
  104. local light = lightNode:CreateLight()
  105. light.lightType = LIGHT_DIRECTIONAL
  106. light.castShadows = true
  107. light.shadowBias = BiasParameters(0.0001, 0.5)
  108. light.shadowCascade = CascadeParameters(10.0, 50.0, 200.0, 0.0, 0.8)
  109. light.specularIntensity = 0.5
  110. end
  111. if true then
  112. local objectNode = testScene:CreateChild("Floor")
  113. objectNode.position = Vector3(0, -0.5, 0)
  114. objectNode.scale = Vector3(200, 1, 200)
  115. local object = objectNode:CreateStaticModel()
  116. object.model = cache:GetModel("Models/Box.mdl")
  117. object.material = cache:GetMaterial("Materials/StoneTiled.xml")
  118. object.occluder = true
  119. local body = objectNode:CreateRigidBody()
  120. local shape = objectNode:CreateCollisionShape()
  121. shape:SetBox(Vector3(1, 1, 1))
  122. end
  123. for i = 1, 50 do
  124. local objectNode = testScene:CreateChild("Box")
  125. objectNode.position = Vector3(Random() * 180 - 90, 1, Random() * 180 - 90)
  126. objectNode:SetScale(2)
  127. local object = objectNode:CreateStaticModel()
  128. object.model = cache:GetModel("Models/Box.mdl")
  129. object.material = cache:GetMaterial("Materials/Stone.xml")
  130. object.castShadows = true
  131. local body = objectNode:CreateRigidBody()
  132. local shape = objectNode:CreateCollisionShape()
  133. shape:SetBox(Vector3(1, 1, 1))
  134. end
  135. for i = 1, 10 do
  136. local objectNode = testScene:CreateChild("Box")
  137. objectNode.position = Vector3(Random() * 180 - 90, 10, Random() * 180 - 90)
  138. objectNode:SetScale(20)
  139. local object = objectNode:CreateStaticModel()
  140. object.model = cache:GetModel("Models/Box.mdl")
  141. object.material = cache:GetMaterial("Materials/Stone.xml")
  142. object.castShadows = true
  143. object.occluder = true
  144. local body = objectNode:CreateRigidBody()
  145. local shape = objectNode:CreateCollisionShape()
  146. shape:SetBox(Vector3(1, 1, 1))
  147. end
  148. for i = 1, 50 do
  149. local objectNode = testScene:CreateChild("Mushroom")
  150. objectNode.position = Vector3(Random() * 180 - 90, 0, Random() * 180 - 90)
  151. objectNode.rotation = Quaternion(0, Random(360.0), 0)
  152. objectNode:SetScale(5)
  153. local object = objectNode:CreateStaticModel()
  154. object.model = cache:GetModel("Models/Mushroom.mdl")
  155. object.material = cache:GetMaterial("Materials/Mushroom.xml")
  156. object.castShadows = true
  157. local body = objectNode:CreateRigidBody()
  158. local shape = objectNode:CreateCollisionShape()
  159. shape:SetTriangleMesh(object:GetModel())
  160. end
  161. for i = 1, 50 do
  162. local objectNode = testScene:CreateChild("Jack")
  163. objectNode:SetPosition(Vector3(Random() * 180 - 90, 0, Random() * 180 - 90))
  164. objectNode:SetRotation(Quaternion(0, Random() * 360, 0))
  165. local object = objectNode:CreateAnimatedModel()
  166. object.model = cache:GetModel("Models/Jack.mdl")
  167. object.material = cache:GetMaterial("Materials/Jack.xml")
  168. object.castShadows = true
  169. -- Create a capsule shape for detecting collisions
  170. local body = objectNode:CreateRigidBody()
  171. body.phantom = true
  172. local shape = objectNode:CreateCollisionShape()
  173. shape:SetCapsule(0.7, 1.8, Vector3(0.0, 0.9, 0.0))
  174. local ctrl = objectNode:CreateAnimationController()
  175. ctrl:Play("Models/Jack_Walk.ani", 0, true, 0.0)
  176. end
  177. end
  178. function HandleUpdate(eventType, eventData)
  179. local timeStep = eventData:GetFloat("TimeStep")
  180. if ui:GetFocusElement() == nil then
  181. local speedMultiplier = 1.0
  182. if input:GetKeyDown(KEY_LSHIFT) then
  183. speedMultiplier = 5.0
  184. end
  185. if input:GetKeyDown(KEY_LCTRL) then
  186. speedMultiplier = 0.1
  187. end
  188. local speed = timeStep * speedMultiplier
  189. if input:GetKeyDown(KEY_W) then
  190. cameraNode:TranslateRelative(Vector3(0, 0, 10) * speed)
  191. end
  192. if input:GetKeyDown(KEY_S) then
  193. cameraNode:TranslateRelative(Vector3(0, 0, -10) * speed)
  194. end
  195. if input:GetKeyDown(KEY_A) then
  196. cameraNode:TranslateRelative(Vector3(-10, 0, 0) * speed)
  197. end
  198. if input:GetKeyDown(KEY_D) then
  199. cameraNode:TranslateRelative(Vector3(10, 0, 0) * speed)
  200. end
  201. end
  202. end
  203. function HandleKeyDown(eventType, eventData)
  204. local key = eventData:GetInt("Key")
  205. if key == KEY_ESC then
  206. if ui:GetFocusElement() == nil then
  207. engine:Exit()
  208. else
  209. local console = GetConsole()
  210. console:SetVisible(false)
  211. end
  212. end
  213. if key == KEY_F1 then
  214. local console = GetConsole()
  215. console:Toggle()
  216. end
  217. if ui:GetFocusElement() == nil then
  218. if key == KEY_1 then
  219. local quality = renderer:GetTextureQuality()
  220. quality = quality + 1
  221. if quality > 2 then
  222. quality = 0
  223. end
  224. renderer:SetTextureQuality(quality)
  225. end
  226. if key == KEY_2 then
  227. local quality = renderer:GetMaterialQuality()
  228. quality = quality + 1
  229. if quality > 2 then
  230. quality = 0
  231. end
  232. renderer:SetMaterialQuality(quality)
  233. end
  234. if key == KEY_3 then
  235. renderer:SetSpecularLighting(not renderer:GetSpecularLighting())
  236. end
  237. if key == KEY_4 then
  238. renderer:SetDrawShadows(not renderer:GetDrawShadows())
  239. end
  240. if key == KEY_5 then
  241. local size = renderer:GetShadowMapSize()
  242. size = size * 2
  243. if size > 2048 then
  244. size = 512
  245. end
  246. renderer:SetShadowMapSize(size)
  247. end
  248. if key == KEY_6 then
  249. renderer:SetShadowQuality(renderer:GetShadowQuality() + 1)
  250. end
  251. if key == KEY_7 then
  252. local occlusion = renderer:GetMaxOccluderTriangles() > 0
  253. occlusion = not occlusion
  254. if occlusion then
  255. renderer:SetMaxOccluderTriangles(5000)
  256. else
  257. renderer:SetMaxOccluderTriangles(0)
  258. end
  259. end
  260. if key == KEY_8 then
  261. renderer:SetDynamicInstancing(not renderer:GetDynamicInstancing())
  262. end
  263. if key == KEY_SPACE then
  264. drawDebug = drawDebug + 1
  265. if drawDebug > 2 then
  266. drawDebug = 0
  267. end
  268. end
  269. if key == KEY_B then
  270. renderer:GetViewport(0):GetRenderPath():ToggleEnabled("Bloom")
  271. end
  272. if key == KEY_F then
  273. renderer:GetViewport(0):GetRenderPath():ToggleEnabled("EdgeFilter")
  274. end
  275. if key == KEY_O then
  276. camera:SetOrthographic(not camera:IsOrthographic())
  277. end
  278. if key == KEY_T then
  279. local debugHud = GetDebugHud()
  280. debugHud:Toggle(DEBUGHUD_SHOW_PROFILER)
  281. end
  282. if key == KEY_F5 then
  283. local xmlFile = File(context, fileSystem:GetProgramDir() + "Data/Scenes/LuaTestScene.xml", FILE_WRITE)
  284. testScene:SaveXML(xmlFile)
  285. end
  286. if key == KEY_F7 then
  287. local xmlFile = File(context, fileSystem:GetProgramDir() + "Data/Scenes/LuaTestScene.xml", FILE_READ)
  288. if xmlFile:IsOpen() then
  289. testScene:LoadXML(xmlFile)
  290. end
  291. end
  292. --]]
  293. end
  294. end
  295. function HandleMouseMove(eventType, eventData)
  296. local buttons = eventData:GetInt("Buttons")
  297. if buttons == MOUSEB_RIGHT then
  298. local mousedx = eventData:GetInt("DX")
  299. local mousedy = eventData:GetInt("DY")
  300. yaw = yaw + (mousedx / 10.0)
  301. pitch = pitch + (mousedy / 10.0)
  302. if pitch < -90.0 then
  303. pitch = -90.0
  304. end
  305. if pitch > 90.0 then
  306. pitch = 90.0
  307. end
  308. cameraNode:SetRotation(Quaternion(pitch, yaw, 0))
  309. end
  310. end
  311. function HandleMouseButtonDown(eventType, eventData)
  312. local button = eventData:GetInt("Button")
  313. if button == MOUSEB_RIGHT then
  314. local cursor = ui:GetCursor()
  315. cursor:SetVisible(false)
  316. end
  317. -- Test either creating a new physics object or painting a decal (SHIFT down)
  318. if button == MOUSEB_LEFT and ui:GetElementAt(ui:GetCursorPosition(), true) == nil and ui:GetFocusElement() == nil then
  319. if not input:GetQualifierDown(QUAL_SHIFT) then
  320. local eventData = VariantMap()
  321. eventData:SetVector3("Pos", cameraNode:GetPosition())
  322. eventData:SetQuaternion("Rot", cameraNode:GetRotation())
  323. -- If we are the client, send the spawn command as a remote event, else send locally
  324. if runClient then
  325. if network:GetServerConnection() ~= nil then
  326. network:GetServerConnection():SendRemoteEvent("SpawnBox", true, eventData)
  327. end
  328. else
  329. SendEvent("SpawnBox", eventData)
  330. end
  331. else
  332. local pos = ui:GetCursorPosition()
  333. if ui:GetElementAt(pos, true) == nil and testScene:GetOctree() ~= nil then
  334. local cameraRay = camera:GetScreenRay(pos.x / graphics:GetWidth(), pos.y / graphics:GetHeight())
  335. local result = testScene:GetOctree():RaycastSingle(cameraRay, RAY_TRIANGLE, 250.0, DRAWABLE_GEOMETRY)
  336. if result.drawable ~= nil then
  337. local rayHitPos = cameraRay.origin + cameraRay.direction * result.distance
  338. local decal = result.drawable:GetNode():GetDecalSet()
  339. if decal == nil then
  340. decal = result.drawable:GetNode():CreateDecalSet()
  341. decal.material = cache:GetMaterial("Materials/UrhoDecal.xml")
  342. -- Increase max. vertices/indices if the target is skinned
  343. if result.drawable:GetTypeName() == "AnimatedModel" then
  344. decal.maxVertices = 2048
  345. decal.maxIndices = 4096
  346. end
  347. end
  348. decal:AddDecal(result.drawable, rayHitPos, cameraNode:GetWorldRotation(), 0.5, 1.0, 1.0, Vector2(0, 0), Vector2(1, 1))
  349. end
  350. end
  351. end
  352. end
  353. end
  354. function HandleSpawnBox(eventType, eventData)
  355. local position = eventData:GetVector3("Pos")
  356. local rotation = eventData:GetQuaternion("Rot")
  357. local newNode = testScene:CreateChild("")
  358. newNode.position = position
  359. newNode.rotation =rotation
  360. newNode:SetScale(0.2)
  361. local body = newNode:CreateRigidBody()
  362. body.mass = 1.0
  363. body.friction = 1.0
  364. body.linearVelocity = rotation * Vector3(0.0, 1.0, 10.0)
  365. local shape = newNode:CreateCollisionShape()
  366. shape:SetBox(Vector3(1, 1, 1))
  367. local object = newNode:CreateStaticModel()
  368. object.model = cache:GetModel("Models/Box.mdl")
  369. object.material = cache:GetMaterial("Materials/StoneSmall.xml")
  370. object.castShadows = true
  371. object.shadowDistance = 150.0
  372. object.drawDistance = 200.0
  373. end
  374. function HandleMouseButtonUp(eventType, eventData)
  375. if eventData:GetInt("Button") == MOUSEB_RIGHT then
  376. ui:GetCursor():SetVisible(true)
  377. end
  378. end
  379. function HandlePostRenderUpdate()
  380. if engine.headless then
  381. return
  382. end
  383. -- Draw rendering debug geometry without depth test to see the effect of occlusion
  384. if drawDebug == 1 then
  385. renderer:DrawDebugGeometry(false)
  386. end
  387. if drawDebug == 2 then
  388. testScene:GetPhysicsWorld():DrawDebugGeometry(true)
  389. end
  390. local pos = ui.cursorPosition
  391. if ui:GetElementAt(pos, true) == nil and testScene:GetOctree() ~= nil then
  392. local cameraRay = camera:GetScreenRay(pos.x / graphics:GetWidth(), pos.y / graphics:GetHeight())
  393. local result = testScene:GetOctree():RaycastSingle(cameraRay, RAY_TRIANGLE, 250.0, DRAWABLE_GEOMETRY)
  394. if result.drawable ~= nil then
  395. local rayHitPos = cameraRay.origin + cameraRay.direction * result.distance
  396. testScene:GetDebugRenderer():AddBoundingBox(BoundingBox(rayHitPos + Vector3(-0.01, -0.01, -0.01), rayHitPos +
  397. Vector3(0.01, 0.01, 0.01)), Color(1.0, 1.0, 1.0), true)
  398. end
  399. end
  400. end
  401. function HandleClientConnected(eventType, eventData)
  402. local connection = eventData:GetConnection("Connection")
  403. connection.scene = testScene -- Begin scene replication to the client
  404. connection.logStatistics = true
  405. end
  406. function HandlePhysicsCollision(eventType, eventData)
  407. -- Check if either of the nodes has an AnimatedModel component
  408. local nodeA = eventData:GetNode("NodeA")
  409. local nodeB = eventData:GetNode("NodeB")
  410. if nodeA:HasComponent("AnimatedModel") then
  411. HandleHit(nodeA)
  412. elseif nodeB:HasComponent("AnimatedModel") then
  413. HandleHit(nodeB)
  414. end
  415. end
  416. function HandleHit(node)
  417. -- Remove the trigger physics shape, and create the ragdoll
  418. node:RemoveComponent("RigidBody")
  419. node:RemoveComponent("CollisionShape")
  420. CreateRagdoll(node:GetAnimatedModel())
  421. end
  422. function CreateRagdoll(model)
  423. local root = model:GetNode()
  424. CreateRagdollBone(root, "Bip01_Pelvis", SHAPE_BOX, Vector3(0.3, 0.2, 0.25), Vector3(0, 0, 0), Quaternion(0, 0, 0))
  425. CreateRagdollBone(root, "Bip01_Spine1", SHAPE_BOX, Vector3(0.35, 0.2, 0.3), Vector3(0.15, 0, 0), Quaternion(0, 0, 0))
  426. CreateRagdollBone(root, "Bip01_L_Thigh", SHAPE_CAPSULE, Vector3(0.175, 0.45, 0.175), Vector3(0.25, 0, 0), Quaternion(0, 0, 90))
  427. CreateRagdollBone(root, "Bip01_R_Thigh", SHAPE_CAPSULE, Vector3(0.175, 0.45, 0.175), Vector3(0.25, 0, 0), Quaternion(0, 0, 90))
  428. CreateRagdollBone(root, "Bip01_L_Calf", SHAPE_CAPSULE, Vector3(0.15, 0.55, 0.15), Vector3(0.25, 0, 0), Quaternion(0, 0, 90))
  429. CreateRagdollBone(root, "Bip01_R_Calf", SHAPE_CAPSULE, Vector3(0.15, 0.55, 0.15), Vector3(0.25, 0, 0), Quaternion(0, 0, 90))
  430. CreateRagdollBone(root, "Bip01_Head", SHAPE_BOX, Vector3(0.2, 0.2, 0.2), Vector3(0.1, 0, 0), Quaternion(0, 0, 0))
  431. CreateRagdollBone(root, "Bip01_L_UpperArm", SHAPE_CAPSULE, Vector3(0.15, 0.35, 0.15), Vector3(0.1, 0, 0), Quaternion(0, 0, 90))
  432. CreateRagdollBone(root, "Bip01_R_UpperArm", SHAPE_CAPSULE, Vector3(0.15, 0.35, 0.15), Vector3(0.1, 0, 0), Quaternion(0, 0, 90))
  433. CreateRagdollBone(root, "Bip01_L_Forearm", SHAPE_CAPSULE, Vector3(0.125, 0.4, 0.125), Vector3(0.2, 0, 0), Quaternion(0, 0, 90))
  434. CreateRagdollBone(root, "Bip01_R_Forearm", SHAPE_CAPSULE, Vector3(0.125, 0.4, 0.125), Vector3(0.2, 0, 0), Quaternion(0, 0, 90))
  435. CreateRagdollConstraint(root, "Bip01_L_Thigh", "Bip01_Pelvis", CONSTRAINT_CONETWIST, Vector3(0, 0, -1), Vector3(0, 0, 1), Vector2(45, 45), Vector2(0, 0), true)
  436. CreateRagdollConstraint(root, "Bip01_R_Thigh", "Bip01_Pelvis", CONSTRAINT_CONETWIST, Vector3(0, 0, -1), Vector3(0, 0, 1), Vector2(45, 45), Vector2(0, 0), true)
  437. CreateRagdollConstraint(root, "Bip01_L_Calf", "Bip01_L_Thigh", CONSTRAINT_HINGE, Vector3(0, 0, -1), Vector3(0, 0, -1), Vector2(90, 0), Vector2(0, 0), true)
  438. CreateRagdollConstraint(root, "Bip01_R_Calf", "Bip01_R_Thigh", CONSTRAINT_HINGE, Vector3(0, 0, -1), Vector3(0, 0, -1), Vector2(90, 0), Vector2(0, 0), true)
  439. CreateRagdollConstraint(root, "Bip01_Spine1", "Bip01_Pelvis", CONSTRAINT_HINGE, Vector3(0, 0, 1), Vector3(0, 0, 1), Vector2(45, 0), Vector2(-10, 0), true)
  440. CreateRagdollConstraint(root, "Bip01_Head", "Bip01_Spine1", CONSTRAINT_CONETWIST, Vector3(1, 0, 0), Vector3(1, 0, 0), Vector2(0, 30), Vector2(0, 0), true)
  441. CreateRagdollConstraint(root, "Bip01_L_UpperArm", "Bip01_Spine1", CONSTRAINT_CONETWIST, Vector3(0, -1, 0), Vector3(0, 1, 0), Vector2(45, 45), Vector2(0, 0), false)
  442. CreateRagdollConstraint(root, "Bip01_R_UpperArm", "Bip01_Spine1", CONSTRAINT_CONETWIST, Vector3(0, -1, 0), Vector3(0, 1, 0), Vector2(45, 45), Vector2(0, 0), false)
  443. CreateRagdollConstraint(root, "Bip01_L_Forearm", "Bip01_L_UpperArm", CONSTRAINT_HINGE, Vector3(0, 0, -1), Vector3(0, 0, -1), Vector2(90, 0), Vector2(0, 0), true)
  444. CreateRagdollConstraint(root, "Bip01_R_Forearm", "Bip01_R_UpperArm", CONSTRAINT_HINGE, Vector3(0, 0, -1), Vector3(0, 0, -1), Vector2(90, 0), Vector2(0, 0), true)
  445. -- Disable animation from all bones (both physical and non-physical) to not interfere
  446. local skel = model:GetSkeleton()
  447. for i = 1, skel:GetNumBones() do
  448. skel:GetBone(i-1).animated = false
  449. end
  450. end
  451. function CreateRagdollBone(root, boneName, type, size, position, rotation)
  452. local boneNode = root:GetChild(boneName, true)
  453. if boneNode == nil or boneNode:HasComponent("RigidBody") then
  454. return
  455. end
  456. -- In networked operation both client and server detect collisions separately, and create ragdolls on their own
  457. -- (bones are not synced over network.) To prevent replicated component ID range clashes when the client creates
  458. -- any components, it is important that the LOCAL creation mode is specified.
  459. local body = boneNode:CreateRigidBody(LOCAL)
  460. body.mass = 1.0
  461. body.linearDamping = 0.05
  462. body.angularDamping = 0.85
  463. body.linearRestThreshold = 1.5
  464. body.angularRestThreshold = 2.5
  465. local shape = boneNode:CreateCollisionShape(LOCAL)
  466. shape.shapeType = type
  467. shape.size = size
  468. shape.position = position
  469. shape.rotation = rotation
  470. end
  471. function CreateRagdollConstraint(root, boneName, parentName, type, axis, parentAxis, highLimit, lowLimit, disableCollision)
  472. local boneNode = root:GetChild(boneName, true)
  473. local parentNode = root:GetChild(parentName, true)
  474. if boneNode == nil or parentNode == nil or boneNode:HasComponent("Constraint") then
  475. return
  476. end
  477. local constraint = boneNode:CreateConstraint(LOCAL)
  478. constraint.constraintType = type
  479. constraint.disableCollision = disableCollision
  480. -- The connected body must be specified before setting the world position
  481. constraint.otherBody = parentNode:GetRigidBody()
  482. constraint.worldPosition = boneNode.worldPosition
  483. constraint:SetAxis(axis)
  484. constraint:SetOtherAxis(parentAxis)
  485. constraint.highLimit = highLimit
  486. constraint.lowLimit = lowLimit
  487. end