unit.lua 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393
  1. -- Copyright (c) 2012-2025 Daniele Bartolini et al.
  2. -- SPDX-License-Identifier: MIT
  3. require "core/lua/class"
  4. UnitUtils = UnitUtils or {}
  5. function UnitUtils.collect_children(scene_graph, unit_id, children)
  6. local transform = SceneGraph.instance(scene_graph, unit_id)
  7. if transform == nil then return end
  8. local cur_child = SceneGraph.first_child(scene_graph, transform)
  9. while cur_child ~= nil do
  10. local child_id = SceneGraph.owner(scene_graph, cur_child)
  11. UnitUtils.collect_children(scene_graph, child_id, children)
  12. cur_child = SceneGraph.next_sibling(scene_graph, cur_child)
  13. end
  14. table.insert(children, unit_id)
  15. end
  16. function UnitUtils.freeze(world, unit_id)
  17. local physics_world = World.physics_world(world)
  18. local scene_graph = World.scene_graph(world)
  19. local children = {}
  20. UnitUtils.collect_children(scene_graph, unit_id, children)
  21. for _, child_id in ipairs(children) do
  22. local actor = PhysicsWorld.actor_instance(physics_world, child_id)
  23. if actor ~= nil then
  24. PhysicsWorld.actor_set_kinematic(physics_world, actor, true)
  25. PhysicsWorld.actor_disable_collision(physics_world, actor)
  26. end
  27. end
  28. end
  29. function UnitUtils.destroy_tree(world, unit_id)
  30. local scene_graph = World.scene_graph(world)
  31. local children = {}
  32. UnitUtils.collect_children(scene_graph, unit_id, children)
  33. for _, child_id in ipairs(children) do
  34. World.destroy_unit(world, child_id)
  35. end
  36. end
  37. UnitBox = class(UnitBox)
  38. function UnitBox:init(world, id, unit_id, prefab)
  39. self._world = world
  40. self._rw = World.render_world(world)
  41. self._id = id
  42. self._unit_id = unit_id
  43. self._prefab = prefab
  44. self._sg = World.scene_graph(world)
  45. self._selected = false
  46. self._obb = { pose = Matrix4x4Box(), half_extents = Vector3Box(), dirty = true }
  47. UnitUtils.freeze(world, unit_id)
  48. end
  49. function UnitBox:id()
  50. return self._id
  51. end
  52. function UnitBox:prefab()
  53. return self._prefab
  54. end
  55. function UnitBox:unit_id()
  56. return self._unit_id
  57. end
  58. function UnitBox:destroy()
  59. UnitUtils.destroy_tree(self._world, self._unit_id)
  60. end
  61. function UnitBox:local_position()
  62. local tr = SceneGraph.instance(self._sg, self._unit_id)
  63. return tr and SceneGraph.local_position(self._sg, tr) or Vector3.zero()
  64. end
  65. function UnitBox:local_rotation()
  66. local tr = SceneGraph.instance(self._sg, self._unit_id)
  67. return tr and SceneGraph.local_rotation(self._sg, tr) or Quaternion.identity()
  68. end
  69. function UnitBox:local_scale()
  70. local tr = SceneGraph.instance(self._sg, self._unit_id)
  71. return tr and SceneGraph.local_scale(self._sg, tr) or Vector3(1, 1, 1)
  72. end
  73. function UnitBox:local_pose()
  74. local tr = SceneGraph.instance(self._sg, self._unit_id)
  75. return tr and SceneGraph.local_pose(self._sg, tr) or Matrix4x4.identity()
  76. end
  77. function UnitBox:world_position()
  78. local tr = SceneGraph.instance(self._sg, self._unit_id)
  79. return tr and SceneGraph.world_position(self._sg, tr) or Vector3.zero()
  80. end
  81. function UnitBox:world_rotation()
  82. local tr = SceneGraph.instance(self._sg, self._unit_id)
  83. return tr and SceneGraph.world_rotation(self._sg, tr) or Quaternion.identity()
  84. end
  85. function UnitBox:world_scale()
  86. return Vector3(1, 1, 1)
  87. end
  88. function UnitBox:world_pose()
  89. local tr = SceneGraph.instance(self._sg, self._unit_id)
  90. return tr and SceneGraph.world_pose(self._sg, tr) or Matrix4x4.identity()
  91. end
  92. function UnitBox:set_local_position(pos)
  93. local tr = SceneGraph.instance(self._sg, self._unit_id)
  94. if tr then
  95. SceneGraph.set_local_position(self._sg, tr, pos)
  96. self._obb.dirty = true
  97. end
  98. end
  99. function UnitBox:set_local_rotation(rot)
  100. local tr = SceneGraph.instance(self._sg, self._unit_id)
  101. if tr then
  102. SceneGraph.set_local_rotation(self._sg, tr, rot)
  103. self._obb.dirty = true
  104. end
  105. end
  106. function UnitBox:set_local_scale(scale)
  107. local tr = SceneGraph.instance(self._sg, self._unit_id)
  108. if tr then
  109. SceneGraph.set_local_scale(self._sg, tr, scale)
  110. self._obb.dirty = true
  111. end
  112. end
  113. function UnitBox:set_local_pose(pose)
  114. local tr = SceneGraph.instance(self._sg, self._unit_id)
  115. if tr then
  116. SceneGraph.set_local_pose(self._sg, tr, pose)
  117. self._obb.dirty = true
  118. end
  119. end
  120. function UnitBox:on_selected(selected)
  121. self._selected = selected
  122. -- Select this unit and all its children.
  123. local scene_graph = self._sg
  124. local unit_id = self._unit_id
  125. local transform = SceneGraph.instance(scene_graph, unit_id)
  126. if transform == nil then
  127. return
  128. end
  129. local cur_child = SceneGraph.first_child(scene_graph, transform)
  130. while cur_child ~= nil do
  131. local child_id = SceneGraph.owner(scene_graph, cur_child)
  132. RenderWorld.selection(self._rw, child_id, selected);
  133. cur_child = SceneGraph.next_sibling(scene_graph, cur_child)
  134. end
  135. RenderWorld.selection(self._rw, self._unit_id, selected);
  136. end
  137. function UnitBox:mesh_tree_obb()
  138. local scene_graph = self._sg
  139. local unit_id = self._unit_id
  140. local obb_tm = self:local_pose()
  141. local obb_he = Vector3(0.01, 0.01, 0.01)
  142. local child_id = unit_id
  143. local mesh = RenderWorld.mesh_instance(self._rw, child_id)
  144. if mesh then
  145. obb_tm, obb_he = Math.obb_merge(obb_tm, obb_he, RenderWorld.mesh_obb(self._rw, mesh))
  146. end
  147. local transform = SceneGraph.instance(scene_graph, unit_id)
  148. if transform ~= nil then
  149. local cur_child = SceneGraph.first_child(scene_graph, transform)
  150. while cur_child ~= nil do
  151. child_id = SceneGraph.owner(scene_graph, cur_child)
  152. mesh = RenderWorld.mesh_instance(self._rw, child_id)
  153. if mesh then
  154. obb_tm, obb_he = Math.obb_merge(obb_tm, obb_he, RenderWorld.mesh_obb(self._rw, mesh))
  155. end
  156. cur_child = SceneGraph.next_sibling(scene_graph, cur_child)
  157. end
  158. end
  159. return obb_tm, obb_he
  160. end
  161. function UnitBox:sprite_tree_obb()
  162. local scene_graph = self._sg
  163. local unit_id = self._unit_id
  164. local obb_tm = self:local_pose()
  165. local obb_he = Vector3(0.01, 0.01, 0.01)
  166. local child_id = unit_id
  167. local mesh = RenderWorld.sprite_instance(self._rw, child_id)
  168. if mesh then
  169. obb_tm, obb_he = Math.obb_merge(obb_tm, obb_he, RenderWorld.sprite_obb(self._rw, mesh))
  170. end
  171. local transform = SceneGraph.instance(scene_graph, unit_id)
  172. if transform ~= nil then
  173. local cur_child = SceneGraph.first_child(scene_graph, transform)
  174. while cur_child ~= nil do
  175. child_id = SceneGraph.owner(scene_graph, cur_child)
  176. mesh = RenderWorld.sprite_instance(self._rw, child_id)
  177. if mesh then
  178. obb_tm, obb_he = Math.obb_merge(obb_tm, obb_he, RenderWorld.sprite_obb(self._rw, mesh))
  179. end
  180. cur_child = SceneGraph.next_sibling(scene_graph, cur_child)
  181. end
  182. end
  183. return obb_tm, obb_he
  184. end
  185. -- Returns the Oriented Bounding-Box of the unit.
  186. function UnitBox:obb()
  187. if self._obb.dirty then
  188. local obb_tm, obb_he = self:mesh_tree_obb()
  189. obb_tm, obb_he = Math.obb_merge(obb_tm, obb_he, self:sprite_tree_obb())
  190. self._obb.pose:store(obb_tm)
  191. self._obb.half_extents:store(obb_he)
  192. self._obb.dirty = false
  193. end
  194. return self._obb.pose:unbox(), self._obb.half_extents:unbox()
  195. end
  196. function UnitBox:raycast_mesh_tree(pos, dir)
  197. local scene_graph = self._sg
  198. local unit_id = self._unit_id
  199. local t_min = math.huge
  200. -- Raycast meshes in this unit and all its children.
  201. local child_id = unit_id
  202. local mesh = RenderWorld.mesh_instance(self._rw, child_id)
  203. if mesh then
  204. local t = RenderWorld.mesh_cast_ray(self._rw, mesh, pos, dir)
  205. if t ~= -1.0 then
  206. t_min = math.min(t, t_min)
  207. end
  208. end
  209. local transform = SceneGraph.instance(scene_graph, unit_id)
  210. if transform == nil then
  211. return -1.0
  212. end
  213. local cur_child = SceneGraph.first_child(scene_graph, transform)
  214. while cur_child ~= nil do
  215. child_id = SceneGraph.owner(scene_graph, cur_child)
  216. mesh = RenderWorld.mesh_instance(self._rw, child_id)
  217. if mesh then
  218. t = RenderWorld.mesh_cast_ray(self._rw, mesh, pos, dir)
  219. if t ~= -1.0 then
  220. t_min = math.min(t, t_min)
  221. end
  222. end
  223. cur_child = SceneGraph.next_sibling(scene_graph, cur_child)
  224. end
  225. return t_min == math.huge and -1.0 or t_min
  226. end
  227. function UnitBox:raycast_sprite_tree(pos, dir)
  228. local scene_graph = self._sg
  229. local unit_id = self._unit_id
  230. local t_min = math.huge
  231. -- Raycast sprites in this unit and all its children.
  232. local child_id = unit_id
  233. local mesh = RenderWorld.sprite_instance(self._rw, child_id)
  234. if mesh then
  235. local t = RenderWorld.sprite_cast_ray(self._rw, mesh, pos, dir)
  236. if t ~= -1.0 then
  237. t_min = math.min(t, t_min)
  238. end
  239. end
  240. local transform = SceneGraph.instance(scene_graph, unit_id)
  241. if transform == nil then
  242. return -1.0
  243. end
  244. local cur_child = SceneGraph.first_child(scene_graph, transform)
  245. while cur_child ~= nil do
  246. child_id = SceneGraph.owner(scene_graph, cur_child)
  247. mesh = RenderWorld.sprite_instance(self._rw, child_id)
  248. if mesh then
  249. t = RenderWorld.sprite_cast_ray(self._rw, mesh, pos, dir)
  250. if t ~= -1.0 then
  251. t_min = math.min(t, t_min)
  252. end
  253. end
  254. cur_child = SceneGraph.next_sibling(scene_graph, cur_child)
  255. end
  256. return t_min == math.huge and -1.0 or t_min
  257. end
  258. function UnitBox:raycast(pos, dir)
  259. local obb_tm, obb_he = self:obb()
  260. if Math.ray_obb_intersection(pos, dir, obb_tm, obb_he) == -1.0 then
  261. return -1.0
  262. end
  263. local t = self:raycast_mesh_tree(pos, dir)
  264. if t ~= -1.0 then
  265. return t
  266. end
  267. return self:raycast_sprite_tree(pos, dir)
  268. end
  269. function UnitBox:draw()
  270. if not self._selected then
  271. return
  272. end
  273. local light = RenderWorld.light_instance(self._rw, self._unit_id)
  274. if light then
  275. RenderWorld.light_debug_draw(self._rw, light, LevelEditor._lines)
  276. end
  277. end
  278. function UnitBox:set_light(type, range, intensity, angle, color, bias, cast_shadows)
  279. local light = RenderWorld.light_instance(self._rw, self._unit_id)
  280. if light then
  281. RenderWorld.light_set_type(self._rw, light, type)
  282. RenderWorld.light_set_color(self._rw, light, color)
  283. RenderWorld.light_set_range(self._rw, light, range)
  284. RenderWorld.light_set_intensity(self._rw, light, intensity)
  285. RenderWorld.light_set_spot_angle(self._rw, light, angle)
  286. RenderWorld.light_set_shadow_bias(self._rw, light, bias)
  287. RenderWorld.light_set_cast_shadows(self._rw, light, cast_shadows)
  288. end
  289. end
  290. function UnitBox:set_mesh(mesh_resource, geometry, material, visible, cast_shadows)
  291. local mesh = RenderWorld.mesh_instance(self._rw, self._unit_id)
  292. if mesh then
  293. RenderWorld.mesh_set_geometry(self._rw, mesh, mesh_resource, geometry)
  294. RenderWorld.mesh_set_material(self._rw, mesh, material)
  295. RenderWorld.mesh_set_visible(self._rw, mesh, visible)
  296. RenderWorld.mesh_set_cast_shadows(self._rw, mesh, cast_shadows)
  297. self._obb.dirty = true
  298. end
  299. end
  300. function UnitBox:set_sprite(sprite_resource_name, material, layer, depth, visible)
  301. local sprite = RenderWorld.sprite_instance(self._rw, self._unit_id)
  302. if sprite then
  303. RenderWorld.sprite_set_sprite(self._rw, sprite, sprite_resource_name)
  304. RenderWorld.sprite_set_material(self._rw, sprite, material)
  305. RenderWorld.sprite_set_layer(self._rw, sprite, layer)
  306. RenderWorld.sprite_set_depth(self._rw, sprite, depth)
  307. RenderWorld.sprite_set_visible(self._rw, sprite, visible)
  308. self._obb.dirty = true
  309. end
  310. end
  311. function UnitBox:set_camera(projection, fov, near_range, far_range)
  312. local camera = World.camera_instance(self._world, self._unit_id)
  313. if camera then
  314. World.camera_set_projection_type(self._world, camera, projection)
  315. World.camera_set_fov(self._world, camera, fov)
  316. World.camera_set_near_clip_distance(self._world, camera, near_range)
  317. World.camera_set_far_clip_distance(self._world, camera, far_range)
  318. end
  319. end
  320. function UnitBox:send()
  321. Device.console_send { type = "unit_spawned"
  322. , id = self._id
  323. , name = self:prefab()
  324. , position = self:local_position()
  325. , rotation = self:local_rotation()
  326. , scale = self:local_scale()
  327. }
  328. end