浏览代码

Added skinning example

Björn Ritzl 1 月之前
父节点
当前提交
056bf3eb2f

+ 14 - 0
model/skinning/example.md

@@ -0,0 +1,14 @@
+---
+name: GPU Skinning
+tags: model
+title: GPU Skinning
+brief: This example demonstrates GPU skinning.
+author: Defold Foundation
+thumbnail: skinning_thumb.png
+---
+
+This example shows how the `model_skinned_instanced.material` is used to render and animate many instances of the same model efficiently using instancing and GPU skinning.
+
+![skinning](skinning.png)
+
+Model from the [Universal Animation Library](https://quaternius.itch.io/universal-animation-library).

+ 38 - 0
model/skinning/game.project

@@ -0,0 +1,38 @@
+[bootstrap]
+main_collection = /main/instancing.collectionc
+render = /builtins/render/default.renderc
+
+[script]
+shared_state = 1
+
+[display]
+width = 720
+height = 720
+
+[android]
+input_method = HiddenInputField
+
+[project]
+title = Skinning
+
+[render]
+clear_color_red = 0.195
+clear_color_green = 0.321
+clear_color_blue = 0.517
+
+[collection]
+max_instances = 32765
+
+[model]
+max_count = 30000
+max_bone_matrix_texture_width = 2048
+max_bone_matrix_texture_height = 2048
+
+[html5]
+scale_mode = fit
+show_fullscreen_button = 0
+show_made_with_defold = 0
+
+[input]
+game_binding = /builtins/input/all.input_bindingc
+

+ 4 - 0
model/skinning/input/game.input_binding

@@ -0,0 +1,4 @@
+mouse_trigger {
+  input: MOUSE_BUTTON_1
+  action: "touch"
+}

二进制
model/skinning/main/AnimationLibrary_Standard.glb


+ 148 - 0
model/skinning/main/controls.gui

@@ -0,0 +1,148 @@
+script: "/main/controls.gui_script"
+fonts {
+  name: "default"
+  font: "/builtins/fonts/default.font"
+}
+nodes {
+  position {
+    x: 410.0
+    y: 60.0
+  }
+  size {
+    x: 50.0
+    y: 30.0
+  }
+  color {
+    x: 0.0
+    y: 0.651
+    z: 0.243
+  }
+  type: TYPE_BOX
+  id: "add_10000"
+  inherit_alpha: true
+}
+nodes {
+  size {
+    x: 40.0
+    y: 30.0
+  }
+  type: TYPE_TEXT
+  text: "+10000"
+  font: "default"
+  id: "add_10000_text"
+  parent: "add_10000"
+  inherit_alpha: true
+}
+nodes {
+  position {
+    x: 477.0
+    y: 60.0
+  }
+  size {
+    x: 50.0
+    y: 30.0
+  }
+  color {
+    x: 0.0
+    y: 0.651
+    z: 0.243
+  }
+  type: TYPE_BOX
+  id: "add_1000"
+  inherit_alpha: true
+}
+nodes {
+  size {
+    x: 40.0
+    y: 30.0
+  }
+  type: TYPE_TEXT
+  text: "+1000"
+  font: "default"
+  id: "add_1000_text"
+  parent: "add_1000"
+  inherit_alpha: true
+}
+nodes {
+  position {
+    x: 544.0
+    y: 60.0
+  }
+  size {
+    x: 50.0
+    y: 30.0
+  }
+  color {
+    x: 0.0
+    y: 0.651
+    z: 0.243
+  }
+  type: TYPE_BOX
+  id: "add_100"
+  inherit_alpha: true
+}
+nodes {
+  size {
+    x: 40.0
+    y: 30.0
+  }
+  type: TYPE_TEXT
+  text: "+100"
+  font: "default"
+  id: "add_100_text"
+  parent: "add_100"
+  inherit_alpha: true
+}
+nodes {
+  position {
+    x: 613.0
+    y: 60.0
+  }
+  size {
+    x: 50.0
+    y: 30.0
+  }
+  color {
+    x: 0.961
+    y: 0.286
+    z: 0.0
+  }
+  type: TYPE_BOX
+  id: "remove_100"
+  inherit_alpha: true
+  size_mode: SIZE_MODE_AUTO
+}
+nodes {
+  size {
+    x: 40.0
+    y: 30.0
+  }
+  type: TYPE_TEXT
+  text: "-100"
+  font: "default"
+  id: "remove_100_text"
+  parent: "remove_100"
+  inherit_alpha: true
+}
+nodes {
+  position {
+    x: 16.0
+    y: 572.0
+  }
+  size {
+    x: 200.0
+    y: 100.0
+  }
+  type: TYPE_TEXT
+  text: "CONTROLS\n"
+  "WASD - Move\n"
+  "E,Q - Up, Down\n"
+  "Mouse - Look\n"
+  ""
+  font: "default"
+  id: "text"
+  pivot: PIVOT_W
+  inherit_alpha: true
+}
+material: "/builtins/materials/gui.material"
+adjust_reference: ADJUST_REFERENCE_PARENT

+ 17 - 0
model/skinning/main/controls.gui_script

@@ -0,0 +1,17 @@
+function init(self)
+	msg.post(".", "acquire_input_focus")
+end
+
+function on_input(self, action_id, action)
+	if action.pressed then
+		if gui.pick_node(gui.get_node("add_10000"), action.x or 0, action.y or 0) then
+			msg.post("example", "add", { amount = 10000 })
+		elseif gui.pick_node(gui.get_node("add_1000"), action.x or 0, action.y or 0) then
+			msg.post("example", "add", { amount = 1000 })
+		elseif gui.pick_node(gui.get_node("add_100"), action.x or 0, action.y or 0) then
+			msg.post("example", "add", { amount = 100 })
+		elseif gui.pick_node(gui.get_node("remove_100"), action.x or 0, action.y or 0) then
+			msg.post("example", "remove", { amount = 100 })
+		end
+	end
+end

+ 61 - 0
model/skinning/main/instancing.collection

@@ -0,0 +1,61 @@
+name: "main"
+scale_along_z: 0
+embedded_instances {
+  id: "camera"
+  data: "components {\n"
+  "  id: \"orbit_camera\"\n"
+  "  component: \"/main/orbit_camera.script\"\n"
+  "  properties {\n"
+  "    id: \"zoom_speed\"\n"
+  "    value: \"0.1\"\n"
+  "    type: PROPERTY_TYPE_NUMBER\n"
+  "  }\n"
+  "  properties {\n"
+  "    id: \"rotation_speed\"\n"
+  "    value: \"0.1\"\n"
+  "    type: PROPERTY_TYPE_NUMBER\n"
+  "  }\n"
+  "  properties {\n"
+  "    id: \"offset\"\n"
+  "    value: \"0.0, 4.0, 10.0\"\n"
+  "    type: PROPERTY_TYPE_VECTOR3\n"
+  "  }\n"
+  "}\n"
+  "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: 200.0\\n"
+  "auto_aspect_ratio: 1\\n"
+  "\"\n"
+  "}\n"
+  ""
+}
+embedded_instances {
+  id: "example"
+  data: "components {\n"
+  "  id: \"instancing\"\n"
+  "  component: \"/main/instancing.script\"\n"
+  "}\n"
+  "embedded_components {\n"
+  "  id: \"factory\"\n"
+  "  type: \"factory\"\n"
+  "  data: \"prototype: \\\"/main/man.go\\\"\\n"
+  "\"\n"
+  "}\n"
+  ""
+  position {
+    x: 480.0
+    y: 588.0
+  }
+}
+embedded_instances {
+  id: "controls"
+  data: "components {\n"
+  "  id: \"controls\"\n"
+  "  component: \"/main/controls.gui\"\n"
+  "}\n"
+  ""
+}

+ 67 - 0
model/skinning/main/instancing.script

@@ -0,0 +1,67 @@
+local ANIMATIONS = {
+	"Dance_Loop",
+	"Jog_Fwd_Loop",
+	"Idle_Loop",
+	"Walk_Loop",
+}
+
+local WIDTH = 20
+
+local function spawn(self, amount)
+	for i=1,amount do
+		local count = #self.models + 1
+		local div = count / (WIDTH * WIDTH)
+		local mod = count % (WIDTH * WIDTH)
+		local x = mod % WIDTH
+		local z = math.floor(mod / WIDTH)
+		local y = math.floor(div)
+		local pos = vmath.vector3(x * 1, y * 3, z * -1) + vmath.vector3(-WIDTH / 2, 0, 0)
+		local id = factory.create("#factory", pos)
+		if not id then
+			break
+		end
+		local model_url = msg.url(nil, id, "model")
+		local animation_id = math.random(1, #ANIMATIONS)
+		model.play_anim(model_url, ANIMATIONS[animation_id], go.PLAYBACK_LOOP_FORWARD)
+		self.models[#self.models + 1] = id
+	end
+end
+
+local function remove(self, amount)
+	for i=1, amount do
+		local id = self.models[#self.models]
+		if id then
+			go.delete(id)
+			self.models[#self.models] = nil
+		end
+	end
+end
+
+function init(self)
+	msg.post(".", "acquire_input_focus")
+	math.randomseed(os.clock())
+
+	self.models = {}
+
+	local debug_ui = false
+	if debug_ui then
+		spawn(self, 1)
+	else
+		go.delete("controls")
+		spawn(self, 1000)
+	end
+end
+
+function on_input(self, action_id, action)
+	if action_id == hash("EXAMPLE_1") and action.pressed then
+		spawn(self, 100)
+	end
+end
+
+function on_message(self, message_id, message, sender)
+	if message_id == hash("add") then
+		spawn(self, message.amount)
+	elseif message_id == hash("remove") then
+		remove(self, message.amount)
+	end
+end

+ 27 - 0
model/skinning/main/man.go

@@ -0,0 +1,27 @@
+embedded_components {
+  id: "model"
+  type: "model"
+  data: "mesh: \"/main/AnimationLibrary_Standard.glb\"\n"
+  "skeleton: \"/main/AnimationLibrary_Standard.glb\"\n"
+  "animations: \"/main/AnimationLibrary_Standard.glb\"\n"
+  "default_animation: \"A_TPose\"\n"
+  "name: \"{{NAME}}\"\n"
+  "materials {\n"
+  "  name: \"M_Joints\"\n"
+  "  material: \"/builtins/materials/model_skinned_instanced.material\"\n"
+  "  textures {\n"
+  "    sampler: \"tex0\"\n"
+  "    texture: \"/main/man.png\"\n"
+  "  }\n"
+  "}\n"
+  "materials {\n"
+  "  name: \"M_Main\"\n"
+  "  material: \"/builtins/materials/model_skinned_instanced.material\"\n"
+  "  textures {\n"
+  "    sampler: \"tex0\"\n"
+  "    texture: \"/main/man.png\"\n"
+  "  }\n"
+  "}\n"
+  "create_go_bones: false\n"
+  ""
+}

二进制
model/skinning/main/man.png


+ 50 - 0
model/skinning/main/orbit_camera.script

@@ -0,0 +1,50 @@
+-- The initial zoom level
+go.property("zoom", 3)
+-- The speed of the zoom
+go.property("zoom_speed", 0)
+-- The speed of the rotation
+go.property("rotation_speed", 0.5)
+-- The offset of the camera from the origin
+go.property("offset", vmath.vector3(0, 0, 0))
+
+function init(self)
+	-- Acquire input focus to receive input events
+	msg.post(".", "acquire_input_focus")
+
+	-- Initialize start values
+	self.yaw = go.get(".", "euler.y")
+	self.pitch = go.get(".", "euler.x")
+	self.zoom_offset = 0
+	self.current_yaw = self.yaw
+	self.current_pitch = self.pitch
+	self.current_zoom = self.zoom_offset
+end
+
+function update(self, dt)
+	-- Animate camera rotation and zoom
+	self.current_yaw = vmath.lerp(0.15, self.current_yaw, self.yaw)
+	self.current_pitch = vmath.lerp(0.15, self.current_pitch, self.pitch)
+	self.current_zoom = vmath.lerp(0.15, self.current_zoom, self.zoom_offset)
+
+	-- Calculate rotation and position
+	local camera_yaw = vmath.quat_rotation_y(math.rad(self.current_yaw))
+	local camera_pitch = vmath.quat_rotation_x(math.rad(self.current_pitch))
+	local camera_rotation = camera_yaw * camera_pitch
+	local camera_position = self.offset + vmath.rotate(camera_rotation, vmath.vector3(0, 0, self.zoom + self.current_zoom))
+
+	-- Set camera position and rotation
+	go.set_position(camera_position)
+	go.set_rotation(camera_rotation)
+end
+
+function on_input(self, action_id, action)
+	if (action_id == hash("touch") or action_id == hash("mouse_button_left")) and not action.pressed then
+		self.yaw   = self.yaw   - action.dx * self.rotation_speed
+		self.pitch = self.pitch + action.dy * self.rotation_speed
+	elseif action_id == hash("mouse_wheel_up") then
+		print("mwup")
+		self.zoom_offset = self.zoom_offset - self.zoom * self.zoom_speed
+	elseif action_id == hash("mouse_wheel_down") then
+		self.zoom_offset = self.zoom_offset + self.zoom * self.zoom_speed
+	end
+end

二进制
model/skinning/skinning.png


二进制
model/skinning/skinning_thumb.png