Skip to content

Commit 056bf3e

Browse files
committed
Added skinning example
1 parent 73e81e9 commit 056bf3e

13 files changed

+426
-0
lines changed

model/skinning/example.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
---
2+
name: GPU Skinning
3+
tags: model
4+
title: GPU Skinning
5+
brief: This example demonstrates GPU skinning.
6+
author: Defold Foundation
7+
thumbnail: skinning_thumb.png
8+
---
9+
10+
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.
11+
12+
![skinning](skinning.png)
13+
14+
Model from the [Universal Animation Library](https://quaternius.itch.io/universal-animation-library).

model/skinning/game.project

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
[bootstrap]
2+
main_collection = /main/instancing.collectionc
3+
render = /builtins/render/default.renderc
4+
5+
[script]
6+
shared_state = 1
7+
8+
[display]
9+
width = 720
10+
height = 720
11+
12+
[android]
13+
input_method = HiddenInputField
14+
15+
[project]
16+
title = Skinning
17+
18+
[render]
19+
clear_color_red = 0.195
20+
clear_color_green = 0.321
21+
clear_color_blue = 0.517
22+
23+
[collection]
24+
max_instances = 32765
25+
26+
[model]
27+
max_count = 30000
28+
max_bone_matrix_texture_width = 2048
29+
max_bone_matrix_texture_height = 2048
30+
31+
[html5]
32+
scale_mode = fit
33+
show_fullscreen_button = 0
34+
show_made_with_defold = 0
35+
36+
[input]
37+
game_binding = /builtins/input/all.input_bindingc
38+
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
mouse_trigger {
2+
input: MOUSE_BUTTON_1
3+
action: "touch"
4+
}
6.36 MB
Binary file not shown.

model/skinning/main/controls.gui

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
script: "/main/controls.gui_script"
2+
fonts {
3+
name: "default"
4+
font: "/builtins/fonts/default.font"
5+
}
6+
nodes {
7+
position {
8+
x: 410.0
9+
y: 60.0
10+
}
11+
size {
12+
x: 50.0
13+
y: 30.0
14+
}
15+
color {
16+
x: 0.0
17+
y: 0.651
18+
z: 0.243
19+
}
20+
type: TYPE_BOX
21+
id: "add_10000"
22+
inherit_alpha: true
23+
}
24+
nodes {
25+
size {
26+
x: 40.0
27+
y: 30.0
28+
}
29+
type: TYPE_TEXT
30+
text: "+10000"
31+
font: "default"
32+
id: "add_10000_text"
33+
parent: "add_10000"
34+
inherit_alpha: true
35+
}
36+
nodes {
37+
position {
38+
x: 477.0
39+
y: 60.0
40+
}
41+
size {
42+
x: 50.0
43+
y: 30.0
44+
}
45+
color {
46+
x: 0.0
47+
y: 0.651
48+
z: 0.243
49+
}
50+
type: TYPE_BOX
51+
id: "add_1000"
52+
inherit_alpha: true
53+
}
54+
nodes {
55+
size {
56+
x: 40.0
57+
y: 30.0
58+
}
59+
type: TYPE_TEXT
60+
text: "+1000"
61+
font: "default"
62+
id: "add_1000_text"
63+
parent: "add_1000"
64+
inherit_alpha: true
65+
}
66+
nodes {
67+
position {
68+
x: 544.0
69+
y: 60.0
70+
}
71+
size {
72+
x: 50.0
73+
y: 30.0
74+
}
75+
color {
76+
x: 0.0
77+
y: 0.651
78+
z: 0.243
79+
}
80+
type: TYPE_BOX
81+
id: "add_100"
82+
inherit_alpha: true
83+
}
84+
nodes {
85+
size {
86+
x: 40.0
87+
y: 30.0
88+
}
89+
type: TYPE_TEXT
90+
text: "+100"
91+
font: "default"
92+
id: "add_100_text"
93+
parent: "add_100"
94+
inherit_alpha: true
95+
}
96+
nodes {
97+
position {
98+
x: 613.0
99+
y: 60.0
100+
}
101+
size {
102+
x: 50.0
103+
y: 30.0
104+
}
105+
color {
106+
x: 0.961
107+
y: 0.286
108+
z: 0.0
109+
}
110+
type: TYPE_BOX
111+
id: "remove_100"
112+
inherit_alpha: true
113+
size_mode: SIZE_MODE_AUTO
114+
}
115+
nodes {
116+
size {
117+
x: 40.0
118+
y: 30.0
119+
}
120+
type: TYPE_TEXT
121+
text: "-100"
122+
font: "default"
123+
id: "remove_100_text"
124+
parent: "remove_100"
125+
inherit_alpha: true
126+
}
127+
nodes {
128+
position {
129+
x: 16.0
130+
y: 572.0
131+
}
132+
size {
133+
x: 200.0
134+
y: 100.0
135+
}
136+
type: TYPE_TEXT
137+
text: "CONTROLS\n"
138+
"WASD - Move\n"
139+
"E,Q - Up, Down\n"
140+
"Mouse - Look\n"
141+
""
142+
font: "default"
143+
id: "text"
144+
pivot: PIVOT_W
145+
inherit_alpha: true
146+
}
147+
material: "/builtins/materials/gui.material"
148+
adjust_reference: ADJUST_REFERENCE_PARENT
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
function init(self)
2+
msg.post(".", "acquire_input_focus")
3+
end
4+
5+
function on_input(self, action_id, action)
6+
if action.pressed then
7+
if gui.pick_node(gui.get_node("add_10000"), action.x or 0, action.y or 0) then
8+
msg.post("example", "add", { amount = 10000 })
9+
elseif gui.pick_node(gui.get_node("add_1000"), action.x or 0, action.y or 0) then
10+
msg.post("example", "add", { amount = 1000 })
11+
elseif gui.pick_node(gui.get_node("add_100"), action.x or 0, action.y or 0) then
12+
msg.post("example", "add", { amount = 100 })
13+
elseif gui.pick_node(gui.get_node("remove_100"), action.x or 0, action.y or 0) then
14+
msg.post("example", "remove", { amount = 100 })
15+
end
16+
end
17+
end
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
name: "main"
2+
scale_along_z: 0
3+
embedded_instances {
4+
id: "camera"
5+
data: "components {\n"
6+
" id: \"orbit_camera\"\n"
7+
" component: \"/main/orbit_camera.script\"\n"
8+
" properties {\n"
9+
" id: \"zoom_speed\"\n"
10+
" value: \"0.1\"\n"
11+
" type: PROPERTY_TYPE_NUMBER\n"
12+
" }\n"
13+
" properties {\n"
14+
" id: \"rotation_speed\"\n"
15+
" value: \"0.1\"\n"
16+
" type: PROPERTY_TYPE_NUMBER\n"
17+
" }\n"
18+
" properties {\n"
19+
" id: \"offset\"\n"
20+
" value: \"0.0, 4.0, 10.0\"\n"
21+
" type: PROPERTY_TYPE_VECTOR3\n"
22+
" }\n"
23+
"}\n"
24+
"embedded_components {\n"
25+
" id: \"camera\"\n"
26+
" type: \"camera\"\n"
27+
" data: \"aspect_ratio: 1.0\\n"
28+
"fov: 0.7854\\n"
29+
"near_z: 0.1\\n"
30+
"far_z: 200.0\\n"
31+
"auto_aspect_ratio: 1\\n"
32+
"\"\n"
33+
"}\n"
34+
""
35+
}
36+
embedded_instances {
37+
id: "example"
38+
data: "components {\n"
39+
" id: \"instancing\"\n"
40+
" component: \"/main/instancing.script\"\n"
41+
"}\n"
42+
"embedded_components {\n"
43+
" id: \"factory\"\n"
44+
" type: \"factory\"\n"
45+
" data: \"prototype: \\\"/main/man.go\\\"\\n"
46+
"\"\n"
47+
"}\n"
48+
""
49+
position {
50+
x: 480.0
51+
y: 588.0
52+
}
53+
}
54+
embedded_instances {
55+
id: "controls"
56+
data: "components {\n"
57+
" id: \"controls\"\n"
58+
" component: \"/main/controls.gui\"\n"
59+
"}\n"
60+
""
61+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
local ANIMATIONS = {
2+
"Dance_Loop",
3+
"Jog_Fwd_Loop",
4+
"Idle_Loop",
5+
"Walk_Loop",
6+
}
7+
8+
local WIDTH = 20
9+
10+
local function spawn(self, amount)
11+
for i=1,amount do
12+
local count = #self.models + 1
13+
local div = count / (WIDTH * WIDTH)
14+
local mod = count % (WIDTH * WIDTH)
15+
local x = mod % WIDTH
16+
local z = math.floor(mod / WIDTH)
17+
local y = math.floor(div)
18+
local pos = vmath.vector3(x * 1, y * 3, z * -1) + vmath.vector3(-WIDTH / 2, 0, 0)
19+
local id = factory.create("#factory", pos)
20+
if not id then
21+
break
22+
end
23+
local model_url = msg.url(nil, id, "model")
24+
local animation_id = math.random(1, #ANIMATIONS)
25+
model.play_anim(model_url, ANIMATIONS[animation_id], go.PLAYBACK_LOOP_FORWARD)
26+
self.models[#self.models + 1] = id
27+
end
28+
end
29+
30+
local function remove(self, amount)
31+
for i=1, amount do
32+
local id = self.models[#self.models]
33+
if id then
34+
go.delete(id)
35+
self.models[#self.models] = nil
36+
end
37+
end
38+
end
39+
40+
function init(self)
41+
msg.post(".", "acquire_input_focus")
42+
math.randomseed(os.clock())
43+
44+
self.models = {}
45+
46+
local debug_ui = false
47+
if debug_ui then
48+
spawn(self, 1)
49+
else
50+
go.delete("controls")
51+
spawn(self, 1000)
52+
end
53+
end
54+
55+
function on_input(self, action_id, action)
56+
if action_id == hash("EXAMPLE_1") and action.pressed then
57+
spawn(self, 100)
58+
end
59+
end
60+
61+
function on_message(self, message_id, message, sender)
62+
if message_id == hash("add") then
63+
spawn(self, message.amount)
64+
elseif message_id == hash("remove") then
65+
remove(self, message.amount)
66+
end
67+
end

model/skinning/main/man.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
embedded_components {
2+
id: "model"
3+
type: "model"
4+
data: "mesh: \"/main/AnimationLibrary_Standard.glb\"\n"
5+
"skeleton: \"/main/AnimationLibrary_Standard.glb\"\n"
6+
"animations: \"/main/AnimationLibrary_Standard.glb\"\n"
7+
"default_animation: \"A_TPose\"\n"
8+
"name: \"{{NAME}}\"\n"
9+
"materials {\n"
10+
" name: \"M_Joints\"\n"
11+
" material: \"/builtins/materials/model_skinned_instanced.material\"\n"
12+
" textures {\n"
13+
" sampler: \"tex0\"\n"
14+
" texture: \"/main/man.png\"\n"
15+
" }\n"
16+
"}\n"
17+
"materials {\n"
18+
" name: \"M_Main\"\n"
19+
" material: \"/builtins/materials/model_skinned_instanced.material\"\n"
20+
" textures {\n"
21+
" sampler: \"tex0\"\n"
22+
" texture: \"/main/man.png\"\n"
23+
" }\n"
24+
"}\n"
25+
"create_go_bones: false\n"
26+
""
27+
}

model/skinning/main/man.png

83 Bytes
Loading

0 commit comments

Comments
 (0)