unit.lua 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421
  1. -- Copyright (c) 2012-2026 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. self:freeze()
  48. end
  49. function UnitBox:freeze()
  50. UnitUtils.freeze(self._world, self._unit_id)
  51. end
  52. function UnitBox:id()
  53. return self._id
  54. end
  55. function UnitBox:prefab()
  56. return self._prefab
  57. end
  58. function UnitBox:unit_id()
  59. return self._unit_id
  60. end
  61. function UnitBox:destroy()
  62. UnitUtils.destroy_tree(self._world, self._unit_id)
  63. end
  64. function UnitBox:is_spatial()
  65. return SceneGraph.instance(self._sg, self._unit_id) ~= nil
  66. end
  67. function UnitBox:local_position()
  68. local tr = SceneGraph.instance(self._sg, self._unit_id)
  69. return tr and SceneGraph.local_position(self._sg, tr) or Vector3.zero()
  70. end
  71. function UnitBox:local_rotation()
  72. local tr = SceneGraph.instance(self._sg, self._unit_id)
  73. return tr and SceneGraph.local_rotation(self._sg, tr) or Quaternion.identity()
  74. end
  75. function UnitBox:local_scale()
  76. local tr = SceneGraph.instance(self._sg, self._unit_id)
  77. return tr and SceneGraph.local_scale(self._sg, tr) or Vector3(1, 1, 1)
  78. end
  79. function UnitBox:local_pose()
  80. local tr = SceneGraph.instance(self._sg, self._unit_id)
  81. return tr and SceneGraph.local_pose(self._sg, tr) or Matrix4x4.identity()
  82. end
  83. function UnitBox:world_position()
  84. local tr = SceneGraph.instance(self._sg, self._unit_id)
  85. return tr and SceneGraph.world_position(self._sg, tr) or Vector3.zero()
  86. end
  87. function UnitBox:world_rotation()
  88. local tr = SceneGraph.instance(self._sg, self._unit_id)
  89. return tr and SceneGraph.world_rotation(self._sg, tr) or Quaternion.identity()
  90. end
  91. function UnitBox:world_scale()
  92. return Vector3(1, 1, 1)
  93. end
  94. function UnitBox:world_pose()
  95. local tr = SceneGraph.instance(self._sg, self._unit_id)
  96. return tr and SceneGraph.world_pose(self._sg, tr) or Matrix4x4.identity()
  97. end
  98. function UnitBox:set_local_position(pos)
  99. local tr = SceneGraph.instance(self._sg, self._unit_id)
  100. if tr then
  101. SceneGraph.set_local_position(self._sg, tr, pos)
  102. self._obb.dirty = true
  103. end
  104. end
  105. function UnitBox:set_local_rotation(rot)
  106. local tr = SceneGraph.instance(self._sg, self._unit_id)
  107. if tr then
  108. SceneGraph.set_local_rotation(self._sg, tr, rot)
  109. self._obb.dirty = true
  110. end
  111. end
  112. function UnitBox:set_local_scale(scale)
  113. local tr = SceneGraph.instance(self._sg, self._unit_id)
  114. if tr then
  115. SceneGraph.set_local_scale(self._sg, tr, scale)
  116. self._obb.dirty = true
  117. end
  118. end
  119. function UnitBox:set_local_pose(pose)
  120. local tr = SceneGraph.instance(self._sg, self._unit_id)
  121. if tr then
  122. SceneGraph.set_local_pose(self._sg, tr, pose)
  123. self._obb.dirty = true
  124. end
  125. end
  126. function UnitBox:set_parent(parent_id)
  127. local tr = SceneGraph.instance(self._sg, self._unit_id)
  128. local parent_unit = LevelEditor._objects[parent_id]:unit_id()
  129. local parent_tr = SceneGraph.instance(self._sg, parent_unit)
  130. if tr and parent_tr then
  131. SceneGraph.link(self._sg
  132. , parent_tr
  133. , tr
  134. , self:local_position()
  135. , self:local_rotation()
  136. , self:local_scale()
  137. )
  138. self._obb.dirty = true
  139. end
  140. end
  141. function UnitBox:on_selected(selected)
  142. local scene_graph = self._sg
  143. local unit_id = self._unit_id
  144. local children = {}
  145. UnitUtils.collect_children(scene_graph, unit_id, children)
  146. for _, child_id in ipairs(children) do
  147. RenderWorld.selection(self._rw, child_id, selected);
  148. end
  149. self._selected = selected
  150. end
  151. function UnitBox:mesh_tree_obb()
  152. local scene_graph = self._sg
  153. local unit_id = self._unit_id
  154. local obb_tm = self:local_pose()
  155. local obb_he = Vector3(0.01, 0.01, 0.01)
  156. local children = {}
  157. UnitUtils.collect_children(scene_graph, unit_id, children)
  158. for _, child_id in ipairs(children) do
  159. local mesh = RenderWorld.mesh_instance(self._rw, child_id)
  160. if mesh then
  161. obb_tm, obb_he = Math.obb_merge(obb_tm, obb_he, RenderWorld.mesh_obb(self._rw, mesh))
  162. end
  163. end
  164. return obb_tm, obb_he
  165. end
  166. function UnitBox:sprite_tree_obb()
  167. local scene_graph = self._sg
  168. local unit_id = self._unit_id
  169. local obb_tm = self:local_pose()
  170. local obb_he = Vector3(0.01, 0.01, 0.01)
  171. local children = {}
  172. UnitUtils.collect_children(scene_graph, unit_id, children)
  173. for _, child_id in ipairs(children) do
  174. local sprite = RenderWorld.sprite_instance(self._rw, child_id)
  175. if sprite then
  176. obb_tm, obb_he = Math.obb_merge(obb_tm, obb_he, RenderWorld.sprite_obb(self._rw, sprite))
  177. end
  178. end
  179. return obb_tm, obb_he
  180. end
  181. -- Returns the Oriented Bounding-Box of the unit.
  182. function UnitBox:obb()
  183. if self._obb.dirty then
  184. local obb_tm, obb_he = self:mesh_tree_obb()
  185. obb_tm, obb_he = Math.obb_merge(obb_tm, obb_he, self:sprite_tree_obb())
  186. self._obb.pose:store(obb_tm)
  187. self._obb.half_extents:store(obb_he)
  188. self._obb.dirty = false
  189. end
  190. return self._obb.pose:unbox(), self._obb.half_extents:unbox()
  191. end
  192. function UnitBox:raycast_mesh_tree(pos, dir)
  193. local scene_graph = self._sg
  194. local unit_id = self._unit_id
  195. local t_min = math.huge
  196. local children = {}
  197. UnitUtils.collect_children(scene_graph, unit_id, children)
  198. for _, child_id in ipairs(children) do
  199. local mesh = RenderWorld.mesh_instance(self._rw, child_id)
  200. if mesh then
  201. local t = RenderWorld.mesh_cast_ray(self._rw, mesh, pos, dir)
  202. if t ~= -1.0 then
  203. t_min = math.min(t, t_min)
  204. end
  205. end
  206. end
  207. return t_min == math.huge and -1.0 or t_min
  208. end
  209. function UnitBox:raycast_sprite_tree(pos, dir)
  210. local scene_graph = self._sg
  211. local unit_id = self._unit_id
  212. local t_min = math.huge
  213. local children = {}
  214. UnitUtils.collect_children(scene_graph, unit_id, children)
  215. for _, child_id in ipairs(children) do
  216. local sprite = RenderWorld.sprite_instance(self._rw, child_id)
  217. if sprite then
  218. local t = RenderWorld.sprite_cast_ray(self._rw, sprite, pos, dir)
  219. if t ~= -1.0 then
  220. t_min = math.min(t, t_min)
  221. end
  222. end
  223. end
  224. return t_min == math.huge and -1.0 or t_min
  225. end
  226. function UnitBox:raycast(pos, dir)
  227. local obb_tm, obb_he = self:obb()
  228. if Math.ray_obb_intersection(pos, dir, obb_tm, obb_he) == -1.0 then
  229. return -1.0
  230. end
  231. local t = self:raycast_mesh_tree(pos, dir)
  232. if t ~= -1.0 then
  233. return t
  234. end
  235. return self:raycast_sprite_tree(pos, dir)
  236. end
  237. function UnitBox:draw()
  238. if not self._selected then
  239. return
  240. end
  241. local light = RenderWorld.light_instance(self._rw, self._unit_id)
  242. if light then
  243. RenderWorld.light_debug_draw(self._rw, light, LevelEditor._lines)
  244. end
  245. local physics_world = World.physics_world(self._world)
  246. local mover = PhysicsWorld.mover_instance(physics_world, self._unit_id)
  247. if mover then
  248. PhysicsWorld.mover_debug_draw(physics_world, mover, LevelEditor._lines)
  249. end
  250. end
  251. function UnitBox:set_light(type, range, intensity, angle, color, bias, cast_shadows)
  252. local light = RenderWorld.light_instance(self._rw, self._unit_id)
  253. if light then
  254. RenderWorld.light_set_type(self._rw, light, type)
  255. RenderWorld.light_set_color(self._rw, light, color)
  256. RenderWorld.light_set_range(self._rw, light, range)
  257. RenderWorld.light_set_intensity(self._rw, light, intensity)
  258. RenderWorld.light_set_spot_angle(self._rw, light, angle)
  259. RenderWorld.light_set_shadow_bias(self._rw, light, bias)
  260. RenderWorld.light_set_cast_shadows(self._rw, light, cast_shadows)
  261. end
  262. end
  263. function UnitBox:set_animation_state_machine(state_machine_resource)
  264. local animation_state_machine = World.animation_state_machine(self._world)
  265. local state_machine = AnimationStateMachine.instance(animation_state_machine, self._unit_id)
  266. if state_machine then
  267. AnimationStateMachine.set_state_machine(animation_state_machine, state_machine, state_machine_resource)
  268. end
  269. end
  270. function UnitBox:set_mover(height, radius, max_slope_angle, center)
  271. local physics_world = World.physics_world(self._world)
  272. local mover = PhysicsWorld.mover_instance(physics_world, self._unit_id)
  273. if mover then
  274. PhysicsWorld.mover_set_height(physics_world, mover, height)
  275. PhysicsWorld.mover_set_radius(physics_world, mover, radius)
  276. PhysicsWorld.mover_set_max_slope_angle(physics_world, mover, max_slope_angle)
  277. PhysicsWorld.mover_set_center(physics_world, mover, center)
  278. end
  279. end
  280. function UnitBox:set_fog(color, density, range_min, range_max, sun_blend, enabled)
  281. local fog = RenderWorld.fog_instance(self._rw, self._unit_id)
  282. if fog then
  283. RenderWorld.fog_set_color(self._rw, fog, color)
  284. RenderWorld.fog_set_density(self._rw, fog, density)
  285. RenderWorld.fog_set_range_min(self._rw, fog, range_min)
  286. RenderWorld.fog_set_range_max(self._rw, fog, range_max)
  287. RenderWorld.fog_set_sun_blend(self._rw, fog, sun_blend)
  288. RenderWorld.fog_set_enabled(self._rw, fog, enabled)
  289. end
  290. end
  291. function UnitBox:set_global_lighting(skydome_map, skydome_intensity, ambient_color)
  292. local gl = RenderWorld.global_lighting_instance(self._rw, self._unit_id)
  293. if gl then
  294. RenderWorld.global_lighting_set_skydome_map(self._rw, skydome_map)
  295. RenderWorld.global_lighting_set_skydome_intensity(self._rw, skydome_intensity)
  296. RenderWorld.global_lighting_set_ambient_color(self._rw, ambient_color)
  297. end
  298. end
  299. function UnitBox:set_bloom(enabled, threshold, weight, intensity)
  300. local bloom = RenderWorld.bloom_instance(self._rw, self._unit_id)
  301. if bloom then
  302. RenderWorld.bloom_set_enabled(self._rw, enabled)
  303. RenderWorld.bloom_set_threshold(self._rw, threshold)
  304. RenderWorld.bloom_set_weight(self._rw, weight)
  305. RenderWorld.bloom_set_intensity(self._rw, intensity)
  306. end
  307. end
  308. function UnitBox:set_tonemap(type)
  309. local tonemap = RenderWorld.tonemap_instance(self._rw, self._unit_id)
  310. if tonemap then
  311. RenderWorld.tonemap_set_type(self._rw, type)
  312. end
  313. end
  314. function UnitBox:set_mesh(mesh_resource, geometry, material, visible, cast_shadows)
  315. local mesh = RenderWorld.mesh_instance(self._rw, self._unit_id)
  316. if mesh then
  317. RenderWorld.mesh_set_geometry(self._rw, mesh, mesh_resource, geometry)
  318. RenderWorld.mesh_set_material(self._rw, mesh, material)
  319. RenderWorld.mesh_set_visible(self._rw, mesh, visible)
  320. RenderWorld.mesh_set_cast_shadows(self._rw, mesh, cast_shadows)
  321. self._obb.dirty = true
  322. end
  323. end
  324. function UnitBox:set_sprite(sprite_resource_name, material, layer, depth, visible, flip_x, flip_y)
  325. local sprite = RenderWorld.sprite_instance(self._rw, self._unit_id)
  326. if sprite then
  327. RenderWorld.sprite_set_sprite(self._rw, sprite, sprite_resource_name)
  328. RenderWorld.sprite_set_material(self._rw, sprite, material)
  329. RenderWorld.sprite_set_layer(self._rw, sprite, layer)
  330. RenderWorld.sprite_set_depth(self._rw, sprite, depth)
  331. RenderWorld.sprite_set_visible(self._rw, sprite, visible)
  332. RenderWorld.sprite_flip_x(self._rw, sprite, flip_x)
  333. RenderWorld.sprite_flip_y(self._rw, sprite, flip_y)
  334. self._obb.dirty = true
  335. end
  336. end
  337. function UnitBox:set_camera(projection, fov, near_range, far_range)
  338. local camera = World.camera_instance(self._world, self._unit_id)
  339. if camera then
  340. World.camera_set_projection_type(self._world, camera, projection)
  341. World.camera_set_fov(self._world, camera, fov)
  342. World.camera_set_near_clip_distance(self._world, camera, near_range)
  343. World.camera_set_far_clip_distance(self._world, camera, far_range)
  344. end
  345. end
  346. function UnitBox:send()
  347. Device.console_send { type = "unit_spawned"
  348. , id = self._id
  349. , name = self:prefab()
  350. , position = self:local_position()
  351. , rotation = self:local_rotation()
  352. , scale = self:local_scale()
  353. }
  354. end