|
@@ -0,0 +1,155 @@
|
|
|
+--
|
|
|
+-- Dynamic bounding box - it tracks the bounding box of the objects in the scene
|
|
|
+--
|
|
|
+
|
|
|
+--- Create a new instance
|
|
|
+-- @return table - the bounding box instance
|
|
|
+local function bbox_new()
|
|
|
+ return {
|
|
|
+ objects = {}, -- dict for iteration
|
|
|
+ count = 0,
|
|
|
+ min = vmath.vector3(),
|
|
|
+ max = vmath.vector3()
|
|
|
+ }
|
|
|
+end
|
|
|
+
|
|
|
+--- Add an object to the bounding box
|
|
|
+-- @param bbox table - the bounding box instance
|
|
|
+-- @param obj_id hash - the object id
|
|
|
+-- @param aabb table - the aabb of the object
|
|
|
+local function bbox_add(bbox, obj_id, aabb)
|
|
|
+ if not aabb then
|
|
|
+ aabb = model.get_aabb(msg.url(nil, obj_id, "model"))
|
|
|
+ else
|
|
|
+ assert(types.is_vector3(aabb.min) and types.is_vector3(aabb.max), "AABB is not valid")
|
|
|
+ end
|
|
|
+
|
|
|
+ local entry = {
|
|
|
+ id = obj_id,
|
|
|
+ position = go.get_position(obj_id),
|
|
|
+ aabb = aabb
|
|
|
+ }
|
|
|
+ bbox.objects[obj_id] = entry
|
|
|
+ bbox.count = bbox.count + 1
|
|
|
+end
|
|
|
+
|
|
|
+--- Remove an object from the bounding box
|
|
|
+-- @param bbox table - the bounding box instance
|
|
|
+-- @param obj_id hash - the object id
|
|
|
+local function bbox_remove(bbox, obj_id)
|
|
|
+ bbox.objects[obj_id] = nil
|
|
|
+ bbox.count = bbox.count - 1
|
|
|
+end
|
|
|
+
|
|
|
+--- Update the bounding box
|
|
|
+-- @param bbox table - the bounding box instance
|
|
|
+local function bbox_update_all(bbox)
|
|
|
+ bbox.min = vmath.vector3()
|
|
|
+ bbox.max = vmath.vector3()
|
|
|
+ for _, entry in pairs(bbox.objects) do
|
|
|
+ local pos = go.get_position(entry.id)
|
|
|
+ entry.position = pos
|
|
|
+
|
|
|
+ bbox.min.x = math.min(bbox.min.x, entry.aabb.min.x + pos.x)
|
|
|
+ bbox.min.y = math.min(bbox.min.y, entry.aabb.min.y + pos.y)
|
|
|
+ bbox.min.z = math.min(bbox.min.z, entry.aabb.min.z + pos.z)
|
|
|
+ bbox.max.x = math.max(bbox.max.x, entry.aabb.max.x + pos.x)
|
|
|
+ bbox.max.y = math.max(bbox.max.y, entry.aabb.max.y + pos.y)
|
|
|
+ bbox.max.z = math.max(bbox.max.z, entry.aabb.max.z + pos.z)
|
|
|
+ end
|
|
|
+end
|
|
|
+
|
|
|
+--- Compute the bounding box
|
|
|
+-- @param bbox table - the bounding box instance
|
|
|
+-- @return table - result with {center, min, max, radius}
|
|
|
+local function bbox_compute(bbox)
|
|
|
+ local center = (bbox.min + bbox.max) * 0.5
|
|
|
+ local radius = vmath.length(bbox.max - bbox.min) * 0.5
|
|
|
+ return {
|
|
|
+ center = center,
|
|
|
+ min = bbox.min,
|
|
|
+ max = bbox.max,
|
|
|
+ radius = radius
|
|
|
+ }
|
|
|
+end
|
|
|
+
|
|
|
+--
|
|
|
+-- Helper functions
|
|
|
+--
|
|
|
+
|
|
|
+--- Add a cube to the scene
|
|
|
+-- @param self table - the script instance
|
|
|
+-- @param x number - the x coordinate
|
|
|
+-- @param y number - the y coordinate
|
|
|
+-- @param z number - the z coordinate
|
|
|
+-- @param color string - the color of the cube - "red" or "white"
|
|
|
+local function add_cube(self, x, y, z, color)
|
|
|
+ if self.bbox.count >= sys.get_config_int("model.max_count") then
|
|
|
+ print("Increase `model.max_count` and `physics.max_collision_object_count` values!")
|
|
|
+ return
|
|
|
+ end
|
|
|
+
|
|
|
+ local url = color == "red" and "#factory_box2" or "#factory_box1"
|
|
|
+ local obj_id = factory.create(url, vmath.vector3(x, y, z))
|
|
|
+ bbox_add(self.bbox, obj_id)
|
|
|
+
|
|
|
+ go.animate(msg.url(nil, obj_id, "model"), "tint.w", go.PLAYBACK_ONCE_BACKWARD, 3, go.EASING_INQUAD, 0.5)
|
|
|
+end
|
|
|
+
|
|
|
+--
|
|
|
+-- Main script
|
|
|
+--
|
|
|
+
|
|
|
+function init(self)
|
|
|
+ -- Acquire input focus to receive input events
|
|
|
+ msg.post(".", "acquire_input_focus")
|
|
|
+
|
|
|
+ -- Get the camera default rotation
|
|
|
+ self.camera_euler = go.get("/camera", "euler")
|
|
|
+
|
|
|
+ -- Create a new dynamic bounding box instance
|
|
|
+ self.bbox = bbox_new()
|
|
|
+
|
|
|
+ -- Add some cubes to the scene at (0, 1-5, 0) coordinates
|
|
|
+ for i = 1, 10 do
|
|
|
+ local cube_color = i % 2 == 0 and "red" or "white"
|
|
|
+ add_cube(self, (math.random() - 0.5) * 0.1, i / 2, (math.random() - 0.5) * 0.1, cube_color)
|
|
|
+ end
|
|
|
+ bbox_update_all(self.bbox)
|
|
|
+
|
|
|
+ -- Compute the initial bounding box data
|
|
|
+ self.view = bbox_compute(self.bbox)
|
|
|
+end
|
|
|
+
|
|
|
+function update(self, dt)
|
|
|
+ bbox_update_all(self.bbox)
|
|
|
+
|
|
|
+ -- Current bounding box data
|
|
|
+ local current = bbox_compute(self.bbox)
|
|
|
+
|
|
|
+ -- Animate the values for smooth camera movement
|
|
|
+ local t = 0.05
|
|
|
+ self.view.center = vmath.lerp(t, self.view.center, current.center)
|
|
|
+ self.view.radius = vmath.lerp(t, self.view.radius, current.radius)
|
|
|
+
|
|
|
+ -- Calculate camera position and rotation
|
|
|
+ local camera_yaw = vmath.quat_rotation_y(math.rad(self.camera_euler.y))
|
|
|
+ local camera_pitch = vmath.quat_rotation_x(math.rad(self.camera_euler.x))
|
|
|
+ local camera_rotation = camera_yaw * camera_pitch
|
|
|
+ local camera_zoom = 1.05 * self.view.radius / math.tan(0.5 * go.get("/camera#camera", "fov"))
|
|
|
+ local camera_position = self.view.center + vmath.rotate(camera_rotation, vmath.vector3(0, 0, camera_zoom))
|
|
|
+ go.set("/camera", "position", camera_position)
|
|
|
+ go.set("/camera", "rotation", camera_rotation)
|
|
|
+
|
|
|
+ -- Uncomment to benchmark
|
|
|
+ -- add_cube(self, math.random(-3, 3), 10, math.random(-3, 3))
|
|
|
+ -- add_cube(self, math.random(-3, 3), 10, math.random(-3, 3), "red")
|
|
|
+end
|
|
|
+
|
|
|
+function on_input(self, action_id, action)
|
|
|
+ -- Add a cube to the scene when the mouse button / space key is pressed
|
|
|
+ if (action_id == hash("touch") or action_id == hash("key_space")) and action.pressed then
|
|
|
+ local colors = {"red", "white"}
|
|
|
+ add_cube(self, (math.random() - 0.5) * 0.5, 10, (math.random() - 0.5) * 0.5, colors[math.random(1, 2)])
|
|
|
+ end
|
|
|
+end
|