aabb.script 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. --
  2. -- Dynamic bounding box - it tracks the bounding box of the objects in the scene
  3. --
  4. --- Create a new instance
  5. -- @return table - the bounding box instance
  6. local function bbox_new()
  7. return {
  8. objects = {}, -- dict for iteration
  9. count = 0,
  10. min = vmath.vector3(),
  11. max = vmath.vector3()
  12. }
  13. end
  14. --- Add an object to the bounding box
  15. -- @param bbox table - the bounding box instance
  16. -- @param obj_id hash - the object id
  17. -- @param aabb table - the aabb of the object
  18. local function bbox_add(bbox, obj_id, aabb)
  19. if not aabb then
  20. aabb = model.get_aabb(msg.url(nil, obj_id, "model"))
  21. else
  22. assert(types.is_vector3(aabb.min) and types.is_vector3(aabb.max), "AABB is not valid")
  23. end
  24. local entry = {
  25. id = obj_id,
  26. position = go.get_position(obj_id),
  27. aabb = aabb
  28. }
  29. bbox.objects[obj_id] = entry
  30. bbox.count = bbox.count + 1
  31. end
  32. --- Remove an object from the bounding box
  33. -- @param bbox table - the bounding box instance
  34. -- @param obj_id hash - the object id
  35. local function bbox_remove(bbox, obj_id)
  36. bbox.objects[obj_id] = nil
  37. bbox.count = bbox.count - 1
  38. end
  39. --- Update the bounding box
  40. -- @param bbox table - the bounding box instance
  41. local function bbox_update_all(bbox)
  42. bbox.min = vmath.vector3()
  43. bbox.max = vmath.vector3()
  44. for _, entry in pairs(bbox.objects) do
  45. local pos = go.get_position(entry.id)
  46. entry.position = pos
  47. bbox.min.x = math.min(bbox.min.x, entry.aabb.min.x + pos.x)
  48. bbox.min.y = math.min(bbox.min.y, entry.aabb.min.y + pos.y)
  49. bbox.min.z = math.min(bbox.min.z, entry.aabb.min.z + pos.z)
  50. bbox.max.x = math.max(bbox.max.x, entry.aabb.max.x + pos.x)
  51. bbox.max.y = math.max(bbox.max.y, entry.aabb.max.y + pos.y)
  52. bbox.max.z = math.max(bbox.max.z, entry.aabb.max.z + pos.z)
  53. end
  54. end
  55. --- Compute the bounding box
  56. -- @param bbox table - the bounding box instance
  57. -- @return table - result with {center, min, max, radius}
  58. local function bbox_compute(bbox)
  59. local center = (bbox.min + bbox.max) * 0.5
  60. local radius = vmath.length(bbox.max - bbox.min) * 0.5
  61. return {
  62. center = center,
  63. min = bbox.min,
  64. max = bbox.max,
  65. radius = radius
  66. }
  67. end
  68. --
  69. -- Helper functions
  70. --
  71. --- Add a cube to the scene
  72. -- @param self table - the script instance
  73. -- @param x number - the x coordinate
  74. -- @param y number - the y coordinate
  75. -- @param z number - the z coordinate
  76. -- @param color string - the color of the cube - "red" or "white"
  77. local function add_cube(self, x, y, z, color)
  78. if self.bbox.count >= sys.get_config_int("model.max_count") then
  79. print("Increase `model.max_count` and `physics.max_collision_object_count` values!")
  80. return
  81. end
  82. local url = color == "red" and "#factory_box2" or "#factory_box1"
  83. local obj_id = factory.create(url, vmath.vector3(x, y, z))
  84. bbox_add(self.bbox, obj_id)
  85. go.animate(msg.url(nil, obj_id, "model"), "tint.w", go.PLAYBACK_ONCE_BACKWARD, 3, go.EASING_INQUAD, 0.5)
  86. end
  87. --
  88. -- Main script
  89. --
  90. function init(self)
  91. -- Acquire input focus to receive input events
  92. msg.post(".", "acquire_input_focus")
  93. -- Get the camera default rotation
  94. self.camera_euler = go.get("/camera", "euler")
  95. -- Create a new dynamic bounding box instance
  96. self.bbox = bbox_new()
  97. -- Add some cubes to the scene at (0, 1-5, 0) coordinates
  98. for i = 1, 10 do
  99. local cube_color = i % 2 == 0 and "red" or "white"
  100. add_cube(self, (math.random() - 0.5) * 0.1, i / 2, (math.random() - 0.5) * 0.1, cube_color)
  101. end
  102. bbox_update_all(self.bbox)
  103. -- Compute the initial bounding box data
  104. self.view = bbox_compute(self.bbox)
  105. end
  106. function update(self, dt)
  107. bbox_update_all(self.bbox)
  108. -- Current bounding box data
  109. local current = bbox_compute(self.bbox)
  110. -- Animate the values for smooth camera movement
  111. local t = 0.05
  112. self.view.center = vmath.lerp(t, self.view.center, current.center)
  113. self.view.radius = vmath.lerp(t, self.view.radius, current.radius)
  114. -- Calculate camera position and rotation
  115. local camera_yaw = vmath.quat_rotation_y(math.rad(self.camera_euler.y))
  116. local camera_pitch = vmath.quat_rotation_x(math.rad(self.camera_euler.x))
  117. local camera_rotation = camera_yaw * camera_pitch
  118. local camera_zoom = 1.05 * self.view.radius / math.tan(0.5 * go.get("/camera#camera", "fov"))
  119. local camera_position = self.view.center + vmath.rotate(camera_rotation, vmath.vector3(0, 0, camera_zoom))
  120. go.set("/camera", "position", camera_position)
  121. go.set("/camera", "rotation", camera_rotation)
  122. -- Uncomment to benchmark
  123. -- add_cube(self, math.random(-3, 3), 10, math.random(-3, 3))
  124. -- add_cube(self, math.random(-3, 3), 10, math.random(-3, 3), "red")
  125. end
  126. function on_input(self, action_id, action)
  127. -- Add a cube to the scene when the mouse button / space key is pressed
  128. if (action_id == hash("touch") or action_id == hash("key_space")) and action.pressed then
  129. local colors = {"red", "white"}
  130. add_cube(self, (math.random() - 0.5) * 0.5, 10, (math.random() - 0.5) * 0.5, colors[math.random(1, 2)])
  131. end
  132. end