Artsiom Trubchyk 3 mesiacov pred
rodič
commit
6bd82a0e3f

+ 18 - 0
model/aabb/all.texture_profiles

@@ -0,0 +1,18 @@
+path_settings {
+  path: "**"
+  profile: "Default"
+}
+profiles {
+  name: "Default"
+  platforms {
+    os: OS_ID_GENERIC
+    formats {
+      format: TEXTURE_FORMAT_RGBA
+      compression_level: BEST
+      compression_type: COMPRESSION_TYPE_DEFAULT
+    }
+    mipmaps: false
+    max_texture_size: 0
+    premultiply_alpha: true
+  }
+}

+ 27 - 0
model/aabb/assets/materials/unlit.fp

@@ -0,0 +1,27 @@
+#version 140
+
+// Inputs should match the vertex shader's outputs.
+in vec2 var_texcoord0;
+
+// The texture to sample.
+uniform lowp sampler2D texture0;
+
+// The final color of the fragment.
+out lowp vec4 final_color;
+
+uniform fs_uniforms
+{
+    mediump vec4 tint;
+};
+
+void main()
+{
+    // Pre-multiply alpha since all runtime textures already are
+    vec4 tint_pm = vec4(tint.xyz * tint.w, tint.w);
+
+    // Sample the texture at the fragment's texture coordinates.
+    vec4 color = texture(texture0, var_texcoord0.xy) * tint_pm;
+
+    // Output the sampled color.
+    final_color = color;
+}

+ 31 - 0
model/aabb/assets/materials/unlit.material

@@ -0,0 +1,31 @@
+name: "unlit"
+tags: "model"
+vertex_program: "/assets/materials/unlit.vp"
+fragment_program: "/assets/materials/unlit.fp"
+vertex_space: VERTEX_SPACE_LOCAL
+vertex_constants {
+  name: "mtx_view"
+  type: CONSTANT_TYPE_VIEW
+}
+vertex_constants {
+  name: "mtx_proj"
+  type: CONSTANT_TYPE_PROJECTION
+}
+fragment_constants {
+  name: "tint"
+  type: CONSTANT_TYPE_USER
+  value {
+    x: 1.0
+    y: 1.0
+    z: 1.0
+    w: 1.0
+  }
+}
+samplers {
+  name: "texture0"
+  wrap_u: WRAP_MODE_CLAMP_TO_EDGE
+  wrap_v: WRAP_MODE_CLAMP_TO_EDGE
+  filter_min: FILTER_MODE_MIN_LINEAR
+  filter_mag: FILTER_MODE_MAG_LINEAR
+  max_anisotropy: 0.0
+}

+ 28 - 0
model/aabb/assets/materials/unlit.vp

@@ -0,0 +1,28 @@
+#version 140
+
+// The model's vertex position and texture coordinates.
+in vec4 position;
+in vec2 texcoord0;
+
+// The model's world matrix.
+in mat4 mtx_world;
+
+// The projection and view matrices.
+uniform general_vp
+{
+    mat4 mtx_view;
+    mat4 mtx_proj;
+};
+
+// The output of a vertex shader are passed to the fragment shader.
+// The texture coordinates of the vertex.
+out vec2 var_texcoord0;
+
+void main()
+{
+    // Pass the texture coordinates to the fragment shader.
+    var_texcoord0 = texcoord0;
+
+    // Transform the vertex position to clip space.
+    gl_Position = mtx_proj * mtx_view * mtx_world * vec4(position.xyz, 1.0);
+}

+ 28 - 0
model/aabb/assets/models/License.txt

@@ -0,0 +1,28 @@
+	
+
+	Prototype Kit (1.0)
+
+	Created/distributed by Kenney (www.kenney.nl)
+	Creation date: 28-08-2024 09:59
+	
+			------------------------------
+
+	License: (Creative Commons Zero, CC0)
+	http://creativecommons.org/publicdomain/zero/1.0/
+
+	You can use this content for personal, educational, and commercial purposes.
+
+	Support by crediting 'Kenney' or 'www.kenney.nl' (this is not a requirement)
+
+			------------------------------
+
+	• Website : www.kenney.nl
+	• Donate  : www.kenney.nl/donate
+
+	• Patreon : patreon.com/kenney
+	
+	Follow on social media for updates:
+
+	• Twitter:   twitter.com/KenneyNL
+	• Instagram: instagram.com/kenney_nl
+	• Mastodon:  mastodon.gamedev.place/@kenney

BIN
model/aabb/assets/models/Textures/colormap.png


BIN
model/aabb/assets/models/crate-color.glb


BIN
model/aabb/assets/models/crate.glb


+ 19 - 0
model/aabb/example.md

@@ -0,0 +1,19 @@
+---
+tags: model
+title: AABB
+brief: This example demonstrates how to use the `model.get_aabb()` function in a 3D scene.
+scripts: aabb.script
+author: Artsiom Trubchyk
+---
+
+This example shows how to work with Axis-Aligned Bounding Boxes (AABB) in a 3D scene. The setup consists of falling cubes that are dynamically tracked by a camera using their combined bounding box. The example demonstrates:
+
+* How to create and manage a dynamic bounding box that updates with moving objects
+* Using `model.get_aabb()` to get object bounds
+* Camera positioning based on bounding box size
+* Dynamic object spawning with factory
+* Smooth camera transitions
+
+Press SPACE or click to spawn new cubes. The camera will automatically adjust to keep all objects in view based on their combined bounding box.
+
+The models used in this example are from Kenney's [Prototype Kit](https://kenney.nl/assets/prototype-kit), licensed under CC0.

+ 97 - 0
model/aabb/example/aabb.collection

@@ -0,0 +1,97 @@
+name: "gltf"
+scale_along_z: 1
+embedded_instances {
+  id: "camera"
+  data: "embedded_components {\n"
+  "  id: \"camera\"\n"
+  "  type: \"camera\"\n"
+  "  data: \"aspect_ratio: 1.0\\n"
+  "fov: 0.7854\\n"
+  "near_z: 0.1\\n"
+  "far_z: 1000.0\\n"
+  "auto_aspect_ratio: 1\\n"
+  "\"\n"
+  "}\n"
+  ""
+  position {
+    x: 1.6733184
+    y: 0.87541014
+    z: -2.2133834
+  }
+  rotation {
+    x: -0.041512698
+    y: 0.93996596
+    z: 0.12374887
+    w: 0.31532025
+  }
+}
+embedded_instances {
+  id: "main"
+  data: "components {\n"
+  "  id: \"aabb\"\n"
+  "  component: \"/example/aabb.script\"\n"
+  "}\n"
+  "embedded_components {\n"
+  "  id: \"factory_box1\"\n"
+  "  type: \"factory\"\n"
+  "  data: \"prototype: \\\"/example/box1.go\\\"\\n"
+  "\"\n"
+  "}\n"
+  "embedded_components {\n"
+  "  id: \"factory_box2\"\n"
+  "  type: \"factory\"\n"
+  "  data: \"prototype: \\\"/example/box2.go\\\"\\n"
+  "\"\n"
+  "}\n"
+  ""
+}
+embedded_instances {
+  id: "ground"
+  data: "embedded_components {\n"
+  "  id: \"sprite\"\n"
+  "  type: \"sprite\"\n"
+  "  data: \"default_animation: \\\"anim\\\"\\n"
+  "material: \\\"/builtins/materials/sprite.material\\\"\\n"
+  "size {\\n"
+  "  x: 40.0\\n"
+  "  y: 40.0\\n"
+  "}\\n"
+  "size_mode: SIZE_MODE_MANUAL\\n"
+  "textures {\\n"
+  "  sampler: \\\"texture_sampler\\\"\\n"
+  "  texture: \\\"/builtins/graphics/particle_blob.tilesource\\\"\\n"
+  "}\\n"
+  "\"\n"
+  "  rotation {\n"
+  "    x: 0.70710677\n"
+  "    w: 0.70710677\n"
+  "  }\n"
+  "}\n"
+  "embedded_components {\n"
+  "  id: \"collisionobject\"\n"
+  "  type: \"collisionobject\"\n"
+  "  data: \"type: COLLISION_OBJECT_TYPE_STATIC\\n"
+  "mass: 0.0\\n"
+  "friction: 0.8\\n"
+  "restitution: 0.2\\n"
+  "group: \\\"default\\\"\\n"
+  "mask: \\\"default\\\"\\n"
+  "embedded_collision_shape {\\n"
+  "  shapes {\\n"
+  "    shape_type: TYPE_BOX\\n"
+  "    position {\\n"
+  "      y: -0.25\\n"
+  "    }\\n"
+  "    rotation {\\n"
+  "    }\\n"
+  "    index: 0\\n"
+  "    count: 3\\n"
+  "  }\\n"
+  "  data: 50.0\\n"
+  "  data: 0.25\\n"
+  "  data: 50.0\\n"
+  "}\\n"
+  "\"\n"
+  "}\n"
+  ""
+}

+ 155 - 0
model/aabb/example/aabb.script

@@ -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

+ 47 - 0
model/aabb/example/box1.go

@@ -0,0 +1,47 @@
+embedded_components {
+  id: "model"
+  type: "model"
+  data: "mesh: \"/assets/models/crate.glb\"\n"
+  "name: \"{{NAME}}\"\n"
+  "materials {\n"
+  "  name: \"colormap\"\n"
+  "  material: \"/assets/materials/unlit.material\"\n"
+  "  textures {\n"
+  "    sampler: \"texture0\"\n"
+  "    texture: \"/assets/models/Textures/colormap.png\"\n"
+  "  }\n"
+  "}\n"
+  ""
+  position {
+    y: -0.25
+  }
+}
+embedded_components {
+  id: "collisionobject"
+  type: "collisionobject"
+  data: "type: COLLISION_OBJECT_TYPE_DYNAMIC\n"
+  "mass: 1.0\n"
+  "friction: 0.9\n"
+  "restitution: 0.3\n"
+  "group: \"default\"\n"
+  "mask: \"default\"\n"
+  "embedded_collision_shape {\n"
+  "  shapes {\n"
+  "    shape_type: TYPE_BOX\n"
+  "    position {\n"
+  "    }\n"
+  "    rotation {\n"
+  "    }\n"
+  "    index: 0\n"
+  "    count: 3\n"
+  "  }\n"
+  "  data: 0.25\n"
+  "  data: 0.25\n"
+  "  data: 0.25\n"
+  "}\n"
+  "linear_damping: 0.001\n"
+  "event_collision: false\n"
+  "event_contact: false\n"
+  "event_trigger: false\n"
+  ""
+}

+ 47 - 0
model/aabb/example/box2.go

@@ -0,0 +1,47 @@
+embedded_components {
+  id: "model"
+  type: "model"
+  data: "mesh: \"/assets/models/crate-color.glb\"\n"
+  "name: \"{{NAME}}\"\n"
+  "materials {\n"
+  "  name: \"colormap\"\n"
+  "  material: \"/assets/materials/unlit.material\"\n"
+  "  textures {\n"
+  "    sampler: \"texture0\"\n"
+  "    texture: \"/assets/models/Textures/colormap.png\"\n"
+  "  }\n"
+  "}\n"
+  ""
+  position {
+    y: -0.25
+  }
+}
+embedded_components {
+  id: "collisionobject"
+  type: "collisionobject"
+  data: "type: COLLISION_OBJECT_TYPE_DYNAMIC\n"
+  "mass: 1.0\n"
+  "friction: 0.9\n"
+  "restitution: 0.3\n"
+  "group: \"default\"\n"
+  "mask: \"default\"\n"
+  "embedded_collision_shape {\n"
+  "  shapes {\n"
+  "    shape_type: TYPE_BOX\n"
+  "    position {\n"
+  "    }\n"
+  "    rotation {\n"
+  "    }\n"
+  "    index: 0\n"
+  "    count: 3\n"
+  "  }\n"
+  "  data: 0.25\n"
+  "  data: 0.25\n"
+  "  data: 0.25\n"
+  "}\n"
+  "linear_damping: 0.001\n"
+  "event_collision: false\n"
+  "event_contact: false\n"
+  "event_trigger: false\n"
+  ""
+}

+ 66 - 0
model/aabb/game.project

@@ -0,0 +1,66 @@
+[project]
+title = model_aabb
+version = 1.0.0
+
+[bootstrap]
+main_collection = /example/aabb.collectionc
+
+[input]
+game_binding = /builtins/input/all.input_bindingc
+repeat_interval = 0.05
+
+[display]
+width = 720
+height = 720
+high_dpi = 1
+update_frequency = 60
+
+[script]
+shared_state = 1
+
+[label]
+subpixels = 1
+
+[sprite]
+subpixels = 1
+
+[windows]
+iap_provider = 
+
+[android]
+package = com.defold.examples
+
+[ios]
+bundle_identifier = com.defold.examples
+
+[osx]
+bundle_identifier = com.defold.examples
+
+[html5]
+show_fullscreen_button = 0
+show_made_with_defold = 0
+scale_mode = no_scale
+heap_size = 64
+
+[graphics]
+texture_profiles = /all.texture_profiles
+
+[render]
+clear_color_blue = 0.8
+clear_color_green = 0.8
+clear_color_red = 0.8
+
+[physics]
+type = 3D
+max_fixed_timesteps = 1
+max_collision_object_count = 1001
+
+[engine]
+max_time_step = 0.05
+
+[model]
+max_count = 1000
+
+[collection]
+max_instances = 1024
+