├── .gitattributes ├── .gitignore ├── README.md ├── assets ├── animations │ ├── CoffeeCupO.glb │ ├── CoffeeCupO.model │ ├── CoffeeCupO_fillup.glb │ ├── CoffeeCupO_idle.glb │ ├── CoffeeCupO_plate.glb │ ├── CoffeeCupO_sugar.glb │ └── cupOdefold.animationset ├── buttons.gui ├── materials │ ├── scene.fp │ ├── scene.material │ └── scene.vp ├── scripts │ ├── button.gui_script │ └── main.script └── textures │ ├── Coffee_CupO_Map.png │ ├── dlab.png │ ├── fill_ico.png │ ├── ico.atlas │ ├── plate_ico.png │ └── sugar_ico.png ├── docs ├── DAGuide.png ├── NLA_exp.png ├── _Blender │ └── CoffeeCupo.blend ├── aniSet.png ├── ani_Model.png ├── blender_NLA.png ├── exSettings1.png ├── glTFexport.png ├── gltf_fps.png ├── mat_world.png ├── model_mat.png ├── new_aniset.png ├── new_model.png └── selectNLAtracks.png ├── game.project ├── input └── game.input_binding ├── main └── main.collection └── render ├── Basic3D.render └── Basic3D.render_script /.gitattributes: -------------------------------------------------------------------------------- 1 | # Defold Protocol Buffer Text Files (https://github.com/github/linguist/issues/5091) 2 | *.animationset linguist-language=JSON5 3 | *.atlas linguist-language=JSON5 4 | *.camera linguist-language=JSON5 5 | *.collection linguist-language=JSON5 6 | *.collectionfactory linguist-language=JSON5 7 | *.collectionproxy linguist-language=JSON5 8 | *.collisionobject linguist-language=JSON5 9 | *.cubemap linguist-language=JSON5 10 | *.display_profiles linguist-language=JSON5 11 | *.factory linguist-language=JSON5 12 | *.font linguist-language=JSON5 13 | *.gamepads linguist-language=JSON5 14 | *.go linguist-language=JSON5 15 | *.gui linguist-language=JSON5 16 | *.input_binding linguist-language=JSON5 17 | *.label linguist-language=JSON5 18 | *.material linguist-language=JSON5 19 | *.mesh linguist-language=JSON5 20 | *.model linguist-language=JSON5 21 | *.particlefx linguist-language=JSON5 22 | *.render linguist-language=JSON5 23 | *.sound linguist-language=JSON5 24 | *.sprite linguist-language=JSON5 25 | *.spinemodel linguist-language=JSON5 26 | *.spinescene linguist-language=JSON5 27 | *.texture_profiles linguist-language=JSON5 28 | *.tilemap linguist-language=JSON5 29 | *.tilesource linguist-language=JSON5 30 | 31 | # Defold JSON Files 32 | *.buffer linguist-language=JSON 33 | 34 | # Defold GLSL Shaders 35 | *.fp linguist-language=GLSL 36 | *.vp linguist-language=GLSL 37 | 38 | # Defold Lua Files 39 | *.editor_script linguist-language=Lua 40 | *.render_script linguist-language=Lua 41 | *.script linguist-language=Lua 42 | *.gui_script linguist-language=Lua 43 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.internal 2 | /build 3 | .externalToolBuilders 4 | .DS_Store 5 | Thumbs.db 6 | .lock-wscript 7 | *.pyc 8 | .project 9 | .cproject 10 | builtins 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ** **UPDATE 5/24/24 This guide still works well however some things have changed you can now save all animation tracks to a single .glTF file and skip exporting one by one and also skip using the .animationset file altogether.** ** 2 | ------------------------ 3 | 4 | # Blender to Defold 3D animation Guide 5 | 6 | [![](/docs/DAGuide.png)](https://github.com/FlexYourBrain/Defold_Animation3D_Guide) 7 | 8 | In this guide we will cover exporting 3d animations from blender then setting them up in an animation set in defold. Using Blender(3.6.1) and Defold(1.4.8) glTF format. 9 | 10 | Some things to note before animating: 11 | - Single root bone for your armature skeleton. 12 | - Single animation per file. Defold cannot use exports with more than one animation. For multiple animations we instead use exports that have data from a single animation and we setup an (.animationset) that links each animation export, then it's used in the (.model) > Animations properties. 13 | - Pre baking animation is not necessary. 14 | 15 | ------ 16 | 17 | So you have some animations ready to go in blender and now you want to bring them into defold. I have created an animated coffee cup model that we will use for this guide. We will need to export a file for the mesh & armature without any animations. This file will be used for the skeleton and mesh set in the model and animationset. Then we will export each individual animation in separate files to use in the animation set for playback in defold. 18 | 19 | I have a rigged coffee cup model that's ready for exporting, lets get started. Select the model and skeleton rig then export to glTF ( File>Export>glTF 2.0 ). 20 | 21 | ![](/docs/glTFexport.png) 22 | 23 | In the settings we will make sure to select the following: 24 | 25 | ![](/docs/exSettings1.png) 26 | 27 | 28 | - Limit the export to Selected Objects instead of the whole scene. 29 | - +Y Up same as in defold. 30 | - When it comes to Mesh Data it is really up to your project if you are going to use some of these settings or not. UV's we will definitely need to draw the textures to the model, multiple UV sets are supported in defold with glTF. Normals you may need in the shader for things like lighting and can be accessed in the shader via normal attribute (`attribute mediump vec3 normal;`). Tangents are supported and can be accessed in the shader via tangent attribute (`attribute mediump vec3 tangent;`). Vertex colors are supported in defold and can be accessed in the shader via color attribute ( `attribute mediump vec4 color;` ). 31 | - Materials we don't need to export because we setup our own materials for use in defold. 32 | - Armature rest position we just use the 0 frame. 33 | - We want to use skinning for our animation. Note: when we use skinned animations in defold our model material must be set to world for vertex space and cannot playback in local space. 34 | - We don't need lighting information I like to set this to Unitless instead of exporting the pbl data. 35 | - We do not use compression in defold and we do not need to check animation for this first export. 36 | 37 | Tip: In blender you can make a preset for these settings. 38 | 39 | Export the mesh and rig to a folder in your 3d project. Next we will move on to the animation exports. A brief description of animation in blender- each animation is setup as an action which you can use the action editor to edit/create these animations. Then the animations actions are stashed into NLA tracks. Each Action can be edited/tweaked when needed by activating them in the action editor. For exporting we will use the NLA editor. Editors are shown here: 40 | 41 | ![](/docs/blender_NLA.png) 42 | 43 | Something to also note is that glTF exports animation playback speed as fps and should be set in blenders output properties > Frame Rate. 44 | 45 | ![](/docs/gltf_fps.png) 46 | 47 | ** Heed This Warning ** Before exporting the animations its important to make sure to backup your blend file before taking the next steps. The reason why is because glTF exports all actions in the NLA editor and in defold we can only use one animation per file. So this means the next steps we take will be to remove actions other than the single one we are exporting and if you accidentally save the blend file while the actions are not stashed in the NLA editor then if you closed blender you would lose the animations because blender will clean up unused action data-blocks by default. If that did happen you could try to restore to previous backup as blender does make backup files. Making backups of your work before exporting you will insure not losing anything from mistakes. 48 | 49 | Ok lets get to exporting an animation. In my coffee cup project I have 4 animations to export and I will begin with the "plating" animation. Starting off by only selecting the skeleton armature then in the NLA editor Shift + Selecting all the actions other than the one I want to export. 50 | 51 | ![](/docs/selectNLAtracks.png) 52 | 53 | Then press X or the Del key to remove the selected action tracks. Now we should be left with one track that we want to export, lets do it. As before we go to ( File>Export>glTF 2.0 ) and we keep the same settings except this time we will tick the animation section and change some things. 54 | 55 | ![](/docs/NLA_exp.png) 56 | 57 | - Change the animation mode to NLA Tracks 58 | - Limit to Playback Range (set in timeline). I also set manual range in NLA track properties for each track. 59 | - Enable sampling 60 | - For the optimization settings you can use some of these depending on your project specifics. Removing duplicate keyframes may create smaller file size but you will be loosing those keys. 61 | - Force Keeping channels for bones; this may be handy if some channels are not being used in the animation yet the channels are keyed. 62 | 63 | Now export the animation to your project. So for any other animations select the armature only and repeat the same steps and settings as before. So now we should have all the exports ready for defold. 64 | 65 | You'll want to have a 3d project started in defold if you didn't already and bring those exports in. If you do not already have one started the basic3d template can be chosen in the NEW PROJECT section when launching defold. Now one thing we need to do is create a material for our animated model. As previously stated we need our material vertex space to be set to world. If needed we can copy the model material from the builtins. 66 | 67 | ![](/docs/model_mat.png) 68 | 69 | Copy and paste it into your project then make sure to change the vertex space to "World".( This material uses diffuse lighting in the shader and may not fit your project. I provided an example project below with unlit material/shader if you need it. ) 70 | 71 | ![](/docs/mat_world.png) 72 | 73 | Next we create an .animationset file by right-clicking in the Assets pane. 74 | 75 | ![](/docs/new_aniset.png) 76 | 77 | In this file we set the first export we created of the mesh and armature in the Skeleton section. Next We add all the animation exports to the Animations section by pressing the + button for each of them. 78 | 79 | ![](/docs/aniSet.png) 80 | 81 | Now we need to create a model by right clicking in the Assets pane and choosing model. 82 | 83 | ![](/docs/new_model.png) 84 | 85 | Then open it up, in the properties pane we can set the mesh and skeleton to the first export we created. Next add the material we created. Then in the Animations here we apply the .animationset file. It's up to your project to choose a default animation or not and of course in the tex0 slot we can apply our models texture. 86 | 87 | ![](/docs/ani_Model.png) 88 | 89 | The model is now setup for animation and you can add this model to a collection. The animation can be played using ` model.play_anim() ` api can be found here: [Model API](https://defold.com/ref/beta/model/) 90 | 91 | ------ 92 | 93 | I created a sample project showing how its all setup and included the blender file which can be found in the `docs/_Blender` folder. 94 | 95 | [GITHUB PROJECT](https://github.com/FlexYourBrain/Defold_Animation3D_Guide) 96 | 97 | [PLAY THE DEMO](https://flexyourbrain.itch.io/defold3danimationguide) 98 | 99 | 100 | 101 | ###### Check out [the documentation pages](https://defold.com/learn) for examples, tutorials, manuals and API docs. 102 | ###### If you run into trouble, help is available in [the Defold forum](https://forum.defold.com). 103 | -------------------------------------------------------------------------------- /assets/animations/CoffeeCupO.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlexYourBrain/Defold_Animation3D_Guide/ea48f38b391ec93d7c125ab31b098cd7f2fee949/assets/animations/CoffeeCupO.glb -------------------------------------------------------------------------------- /assets/animations/CoffeeCupO.model: -------------------------------------------------------------------------------- 1 | mesh: "/assets/animations/CoffeeCupO.glb" 2 | material: "/assets/materials/scene.material" 3 | textures: "/assets/textures/Coffee_CupO_Map.png" 4 | skeleton: "/assets/animations/CoffeeCupO.glb" 5 | animations: "/assets/animations/cupOdefold.animationset" 6 | default_animation: "CoffeeCupO_idle" 7 | name: "plate" 8 | -------------------------------------------------------------------------------- /assets/animations/CoffeeCupO_fillup.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlexYourBrain/Defold_Animation3D_Guide/ea48f38b391ec93d7c125ab31b098cd7f2fee949/assets/animations/CoffeeCupO_fillup.glb -------------------------------------------------------------------------------- /assets/animations/CoffeeCupO_idle.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlexYourBrain/Defold_Animation3D_Guide/ea48f38b391ec93d7c125ab31b098cd7f2fee949/assets/animations/CoffeeCupO_idle.glb -------------------------------------------------------------------------------- /assets/animations/CoffeeCupO_plate.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlexYourBrain/Defold_Animation3D_Guide/ea48f38b391ec93d7c125ab31b098cd7f2fee949/assets/animations/CoffeeCupO_plate.glb -------------------------------------------------------------------------------- /assets/animations/CoffeeCupO_sugar.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlexYourBrain/Defold_Animation3D_Guide/ea48f38b391ec93d7c125ab31b098cd7f2fee949/assets/animations/CoffeeCupO_sugar.glb -------------------------------------------------------------------------------- /assets/animations/cupOdefold.animationset: -------------------------------------------------------------------------------- 1 | animations { 2 | animation: "/assets/animations/CoffeeCupO_idle.glb" 3 | } 4 | animations { 5 | animation: "/assets/animations/CoffeeCupO_plate.glb" 6 | } 7 | animations { 8 | animation: "/assets/animations/CoffeeCupO_fillup.glb" 9 | } 10 | animations { 11 | animation: "/assets/animations/CoffeeCupO_sugar.glb" 12 | } 13 | skeleton: "/assets/animations/CoffeeCupO.glb" 14 | -------------------------------------------------------------------------------- /assets/buttons.gui: -------------------------------------------------------------------------------- 1 | script: "/assets/scripts/button.gui_script" 2 | textures { 3 | name: "ico" 4 | texture: "/assets/textures/ico.atlas" 5 | } 6 | background_color { 7 | x: 0.0 8 | y: 0.0 9 | z: 0.0 10 | w: 0.0 11 | } 12 | nodes { 13 | position { 14 | x: 1244.0 15 | y: 46.0 16 | z: 0.0 17 | w: 1.0 18 | } 19 | rotation { 20 | x: 0.0 21 | y: 0.0 22 | z: 0.0 23 | w: 1.0 24 | } 25 | scale { 26 | x: 1.0 27 | y: 1.0 28 | z: 1.0 29 | w: 1.0 30 | } 31 | size { 32 | x: 200.0 33 | y: 100.0 34 | z: 0.0 35 | w: 1.0 36 | } 37 | color { 38 | x: 1.0 39 | y: 1.0 40 | z: 1.0 41 | w: 1.0 42 | } 43 | type: TYPE_BOX 44 | blend_mode: BLEND_MODE_ALPHA 45 | texture: "ico/dlab" 46 | id: "logo" 47 | xanchor: XANCHOR_NONE 48 | yanchor: YANCHOR_NONE 49 | pivot: PIVOT_CENTER 50 | adjust_mode: ADJUST_MODE_FIT 51 | layer: "" 52 | inherit_alpha: true 53 | slice9 { 54 | x: 0.0 55 | y: 0.0 56 | z: 0.0 57 | w: 0.0 58 | } 59 | clipping_mode: CLIPPING_MODE_NONE 60 | clipping_visible: true 61 | clipping_inverted: false 62 | alpha: 1.0 63 | template_node_child: false 64 | size_mode: SIZE_MODE_AUTO 65 | custom_type: 0 66 | enabled: true 67 | visible: true 68 | material: "" 69 | } 70 | nodes { 71 | position { 72 | x: 134.0 73 | y: 720.0 74 | z: 0.0 75 | w: 1.0 76 | } 77 | rotation { 78 | x: 0.0 79 | y: 0.0 80 | z: 0.0 81 | w: 1.0 82 | } 83 | scale { 84 | x: 1.0 85 | y: 1.0 86 | z: 1.0 87 | w: 1.0 88 | } 89 | size { 90 | x: 200.0 91 | y: 100.0 92 | z: 0.0 93 | w: 1.0 94 | } 95 | color { 96 | x: 1.0 97 | y: 1.0 98 | z: 1.0 99 | w: 1.0 100 | } 101 | type: TYPE_BOX 102 | blend_mode: BLEND_MODE_ALPHA 103 | texture: "ico/plate_ico" 104 | id: "plate" 105 | xanchor: XANCHOR_NONE 106 | yanchor: YANCHOR_NONE 107 | pivot: PIVOT_CENTER 108 | adjust_mode: ADJUST_MODE_FIT 109 | layer: "" 110 | inherit_alpha: true 111 | slice9 { 112 | x: 0.0 113 | y: 0.0 114 | z: 0.0 115 | w: 0.0 116 | } 117 | clipping_mode: CLIPPING_MODE_NONE 118 | clipping_visible: true 119 | clipping_inverted: false 120 | alpha: 1.0 121 | template_node_child: false 122 | size_mode: SIZE_MODE_AUTO 123 | custom_type: 0 124 | enabled: true 125 | visible: true 126 | material: "" 127 | } 128 | nodes { 129 | position { 130 | x: 109.0 131 | y: 582.0 132 | z: 0.0 133 | w: 1.0 134 | } 135 | rotation { 136 | x: 0.0 137 | y: 0.0 138 | z: 0.0 139 | w: 1.0 140 | } 141 | scale { 142 | x: 1.0 143 | y: 1.0 144 | z: 1.0 145 | w: 1.0 146 | } 147 | size { 148 | x: 200.0 149 | y: 100.0 150 | z: 0.0 151 | w: 1.0 152 | } 153 | color { 154 | x: 1.0 155 | y: 1.0 156 | z: 1.0 157 | w: 1.0 158 | } 159 | type: TYPE_BOX 160 | blend_mode: BLEND_MODE_ALPHA 161 | texture: "ico/fill_ico" 162 | id: "fill" 163 | xanchor: XANCHOR_NONE 164 | yanchor: YANCHOR_NONE 165 | pivot: PIVOT_CENTER 166 | adjust_mode: ADJUST_MODE_FIT 167 | layer: "" 168 | inherit_alpha: true 169 | slice9 { 170 | x: 0.0 171 | y: 0.0 172 | z: 0.0 173 | w: 0.0 174 | } 175 | clipping_mode: CLIPPING_MODE_NONE 176 | clipping_visible: true 177 | clipping_inverted: false 178 | alpha: 1.0 179 | template_node_child: false 180 | size_mode: SIZE_MODE_AUTO 181 | custom_type: 0 182 | enabled: true 183 | visible: true 184 | material: "" 185 | } 186 | nodes { 187 | position { 188 | x: 138.0 189 | y: 444.0 190 | z: 0.0 191 | w: 1.0 192 | } 193 | rotation { 194 | x: 0.0 195 | y: 0.0 196 | z: 0.0 197 | w: 1.0 198 | } 199 | scale { 200 | x: 1.0 201 | y: 1.0 202 | z: 1.0 203 | w: 1.0 204 | } 205 | size { 206 | x: 200.0 207 | y: 100.0 208 | z: 0.0 209 | w: 1.0 210 | } 211 | color { 212 | x: 1.0 213 | y: 1.0 214 | z: 1.0 215 | w: 1.0 216 | } 217 | type: TYPE_BOX 218 | blend_mode: BLEND_MODE_ALPHA 219 | texture: "ico/sugar_ico" 220 | id: "sugar" 221 | xanchor: XANCHOR_NONE 222 | yanchor: YANCHOR_NONE 223 | pivot: PIVOT_CENTER 224 | adjust_mode: ADJUST_MODE_FIT 225 | layer: "" 226 | inherit_alpha: true 227 | slice9 { 228 | x: 0.0 229 | y: 0.0 230 | z: 0.0 231 | w: 0.0 232 | } 233 | clipping_mode: CLIPPING_MODE_NONE 234 | clipping_visible: true 235 | clipping_inverted: false 236 | alpha: 1.0 237 | template_node_child: false 238 | size_mode: SIZE_MODE_AUTO 239 | custom_type: 0 240 | enabled: true 241 | visible: true 242 | material: "" 243 | } 244 | material: "/builtins/materials/gui.material" 245 | adjust_reference: ADJUST_REFERENCE_PARENT 246 | max_nodes: 512 247 | -------------------------------------------------------------------------------- /assets/materials/scene.fp: -------------------------------------------------------------------------------- 1 | varying mediump vec2 var_texcoord0; 2 | 3 | uniform lowp sampler2D tex0; 4 | 5 | void main() 6 | { 7 | vec4 color = texture2D(tex0, var_texcoord0.xy); 8 | gl_FragColor = vec4(color.rgb/color.a, color.a); 9 | } -------------------------------------------------------------------------------- /assets/materials/scene.material: -------------------------------------------------------------------------------- 1 | name: "scene" 2 | tags: "model" 3 | vertex_program: "/assets/materials/scene.vp" 4 | fragment_program: "/assets/materials/scene.fp" 5 | vertex_space: VERTEX_SPACE_WORLD 6 | vertex_constants { 7 | name: "mtx_worldview" 8 | type: CONSTANT_TYPE_WORLDVIEW 9 | } 10 | vertex_constants { 11 | name: "mtx_view" 12 | type: CONSTANT_TYPE_VIEW 13 | } 14 | vertex_constants { 15 | name: "mtx_proj" 16 | type: CONSTANT_TYPE_PROJECTION 17 | } 18 | samplers { 19 | name: "tex0" 20 | wrap_u: WRAP_MODE_CLAMP_TO_EDGE 21 | wrap_v: WRAP_MODE_CLAMP_TO_EDGE 22 | filter_min: FILTER_MODE_MIN_LINEAR 23 | filter_mag: FILTER_MODE_MAG_LINEAR 24 | max_anisotropy: 1.0 25 | } 26 | max_page_count: 0 27 | -------------------------------------------------------------------------------- /assets/materials/scene.vp: -------------------------------------------------------------------------------- 1 | 2 | attribute highp vec4 position; 3 | attribute mediump vec2 texcoord0; 4 | 5 | uniform mediump mat4 mtx_worldview; 6 | uniform mediump mat4 mtx_view; 7 | uniform mediump mat4 mtx_proj; 8 | 9 | varying mediump vec2 var_texcoord0; 10 | 11 | void main() 12 | { 13 | vec4 p = mtx_worldview * vec4(position.xyz, 1.0); 14 | var_texcoord0 = texcoord0; 15 | gl_Position = mtx_proj * p; 16 | } 17 | -------------------------------------------------------------------------------- /assets/scripts/button.gui_script: -------------------------------------------------------------------------------- 1 | local fill = "CoffeeCupO_fillup"; local sugar = "CoffeeCupO_sugar"; local plate = "CoffeeCupO_plate" 2 | local bop = vmath.vector3(1.13); local one = 0.25; local z0 = 0; local script = "/camera#default_camera" 3 | 4 | function init(self) 5 | 6 | msg.post(".", "acquire_input_focus") 7 | self.plate = gui.get_node("plate") 8 | self.fill = gui.get_node("fill") 9 | self.sugar = gui.get_node("sugar") 10 | 11 | end 12 | 13 | local function button_press(ani) 14 | gui.animate(ani, "scale", bop, gui.EASING_INOUTSINE, one, z0, nil, gui.PLAYBACK_ONCE_PINGPONG) 15 | end 16 | 17 | function on_input(self, action_id, action) 18 | 19 | if action_id == hash("touch") and action.pressed then 20 | if gui.pick_node(self.plate, action.x, action.y) then 21 | button_press(self.plate) 22 | msg.post(script, "animate", { anim_id = plate}) 23 | end 24 | if gui.pick_node(self.fill, action.x, action.y) then 25 | button_press(self.fill) 26 | msg.post(script, "animate", { anim_id = fill}) 27 | end 28 | if gui.pick_node(self.sugar, action.x, action.y) then 29 | button_press(self.sugar) 30 | msg.post(script, "animate", { anim_id = sugar}) 31 | end 32 | end 33 | 34 | end -------------------------------------------------------------------------------- /assets/scripts/main.script: -------------------------------------------------------------------------------- 1 | local model_url = "/CupO#CoffeeCupO" 2 | 3 | function init(self) 4 | msg.post("@render:", "clear_color", { color = vmath.vector4(0.299, 0.502, 0.830, 0) }) -- Set Clear Color RGBA(Background color) 5 | -- msg.post(".", "acquire_input_focus") 6 | msg.post("@render:", "use_camera_projection") 7 | msg.post("camera", "acquire_camera_focus") 8 | 9 | end 10 | 11 | function on_message(self, message_id, message) 12 | 13 | if message_id == hash("animate") then 14 | model.play_anim(model_url, message.anim_id, go.PLAYBACK_ONCE_FORWARD, { playback_rate = 0.9} ) 15 | end 16 | 17 | end -------------------------------------------------------------------------------- /assets/textures/Coffee_CupO_Map.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlexYourBrain/Defold_Animation3D_Guide/ea48f38b391ec93d7c125ab31b098cd7f2fee949/assets/textures/Coffee_CupO_Map.png -------------------------------------------------------------------------------- /assets/textures/dlab.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlexYourBrain/Defold_Animation3D_Guide/ea48f38b391ec93d7c125ab31b098cd7f2fee949/assets/textures/dlab.png -------------------------------------------------------------------------------- /assets/textures/fill_ico.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlexYourBrain/Defold_Animation3D_Guide/ea48f38b391ec93d7c125ab31b098cd7f2fee949/assets/textures/fill_ico.png -------------------------------------------------------------------------------- /assets/textures/ico.atlas: -------------------------------------------------------------------------------- 1 | images { 2 | image: "/assets/textures/dlab.png" 3 | sprite_trim_mode: SPRITE_TRIM_MODE_OFF 4 | } 5 | images { 6 | image: "/assets/textures/fill_ico.png" 7 | sprite_trim_mode: SPRITE_TRIM_MODE_OFF 8 | } 9 | images { 10 | image: "/assets/textures/plate_ico.png" 11 | sprite_trim_mode: SPRITE_TRIM_MODE_OFF 12 | } 13 | images { 14 | image: "/assets/textures/sugar_ico.png" 15 | sprite_trim_mode: SPRITE_TRIM_MODE_OFF 16 | } 17 | margin: 0 18 | extrude_borders: 2 19 | inner_padding: 0 20 | max_page_width: 0 21 | max_page_height: 0 22 | -------------------------------------------------------------------------------- /assets/textures/plate_ico.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlexYourBrain/Defold_Animation3D_Guide/ea48f38b391ec93d7c125ab31b098cd7f2fee949/assets/textures/plate_ico.png -------------------------------------------------------------------------------- /assets/textures/sugar_ico.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlexYourBrain/Defold_Animation3D_Guide/ea48f38b391ec93d7c125ab31b098cd7f2fee949/assets/textures/sugar_ico.png -------------------------------------------------------------------------------- /docs/DAGuide.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlexYourBrain/Defold_Animation3D_Guide/ea48f38b391ec93d7c125ab31b098cd7f2fee949/docs/DAGuide.png -------------------------------------------------------------------------------- /docs/NLA_exp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlexYourBrain/Defold_Animation3D_Guide/ea48f38b391ec93d7c125ab31b098cd7f2fee949/docs/NLA_exp.png -------------------------------------------------------------------------------- /docs/_Blender/CoffeeCupo.blend: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlexYourBrain/Defold_Animation3D_Guide/ea48f38b391ec93d7c125ab31b098cd7f2fee949/docs/_Blender/CoffeeCupo.blend -------------------------------------------------------------------------------- /docs/aniSet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlexYourBrain/Defold_Animation3D_Guide/ea48f38b391ec93d7c125ab31b098cd7f2fee949/docs/aniSet.png -------------------------------------------------------------------------------- /docs/ani_Model.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlexYourBrain/Defold_Animation3D_Guide/ea48f38b391ec93d7c125ab31b098cd7f2fee949/docs/ani_Model.png -------------------------------------------------------------------------------- /docs/blender_NLA.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlexYourBrain/Defold_Animation3D_Guide/ea48f38b391ec93d7c125ab31b098cd7f2fee949/docs/blender_NLA.png -------------------------------------------------------------------------------- /docs/exSettings1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlexYourBrain/Defold_Animation3D_Guide/ea48f38b391ec93d7c125ab31b098cd7f2fee949/docs/exSettings1.png -------------------------------------------------------------------------------- /docs/glTFexport.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlexYourBrain/Defold_Animation3D_Guide/ea48f38b391ec93d7c125ab31b098cd7f2fee949/docs/glTFexport.png -------------------------------------------------------------------------------- /docs/gltf_fps.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlexYourBrain/Defold_Animation3D_Guide/ea48f38b391ec93d7c125ab31b098cd7f2fee949/docs/gltf_fps.png -------------------------------------------------------------------------------- /docs/mat_world.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlexYourBrain/Defold_Animation3D_Guide/ea48f38b391ec93d7c125ab31b098cd7f2fee949/docs/mat_world.png -------------------------------------------------------------------------------- /docs/model_mat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlexYourBrain/Defold_Animation3D_Guide/ea48f38b391ec93d7c125ab31b098cd7f2fee949/docs/model_mat.png -------------------------------------------------------------------------------- /docs/new_aniset.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlexYourBrain/Defold_Animation3D_Guide/ea48f38b391ec93d7c125ab31b098cd7f2fee949/docs/new_aniset.png -------------------------------------------------------------------------------- /docs/new_model.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlexYourBrain/Defold_Animation3D_Guide/ea48f38b391ec93d7c125ab31b098cd7f2fee949/docs/new_model.png -------------------------------------------------------------------------------- /docs/selectNLAtracks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlexYourBrain/Defold_Animation3D_Guide/ea48f38b391ec93d7c125ab31b098cd7f2fee949/docs/selectNLAtracks.png -------------------------------------------------------------------------------- /game.project: -------------------------------------------------------------------------------- 1 | [bootstrap] 2 | main_collection = /main/main.collectionc 3 | render = /render/Basic3D.renderc 4 | 5 | [script] 6 | shared_state = 1 7 | 8 | [display] 9 | width = 1280 10 | height = 800 11 | 12 | [android] 13 | input_method = HiddenInputField 14 | package = com.defold.example.basic3d 15 | 16 | [project] 17 | dependencies = 18 | title = Defold_Animation3D_Guide 19 | 20 | [physics] 21 | type = 3D 22 | 23 | [osx] 24 | bundle_identifier = com.defold.example.basic3d 25 | 26 | [ios] 27 | bundle_identifier = com.defold.example.basic3d 28 | 29 | [html5] 30 | show_fullscreen_button = 0 31 | show_made_with_defold = 0 32 | 33 | -------------------------------------------------------------------------------- /input/game.input_binding: -------------------------------------------------------------------------------- 1 | mouse_trigger { 2 | input: MOUSE_BUTTON_1 3 | action: "touch" 4 | } 5 | -------------------------------------------------------------------------------- /main/main.collection: -------------------------------------------------------------------------------- 1 | name: "main" 2 | scale_along_z: 0 3 | embedded_instances { 4 | id: "camera" 5 | data: "components {\n" 6 | " id: \"default_camera\"\n" 7 | " component: \"/assets/scripts/main.script\"\n" 8 | " position {\n" 9 | " x: 0.0\n" 10 | " y: 0.0\n" 11 | " z: 0.0\n" 12 | " }\n" 13 | " rotation {\n" 14 | " x: 0.0\n" 15 | " y: 0.0\n" 16 | " z: 0.0\n" 17 | " w: 1.0\n" 18 | " }\n" 19 | " property_decls {\n" 20 | " }\n" 21 | "}\n" 22 | "embedded_components {\n" 23 | " id: \"camera\"\n" 24 | " type: \"camera\"\n" 25 | " data: \"aspect_ratio: 1.0\\n" 26 | "fov: 0.5643\\n" 27 | "near_z: 0.1\\n" 28 | "far_z: 1000.0\\n" 29 | "auto_aspect_ratio: 1\\n" 30 | "orthographic_projection: 0\\n" 31 | "orthographic_zoom: 1.0\\n" 32 | "\"\n" 33 | " position {\n" 34 | " x: 0.0\n" 35 | " y: 0.0\n" 36 | " z: 0.0\n" 37 | " }\n" 38 | " rotation {\n" 39 | " x: 0.0\n" 40 | " y: 0.0\n" 41 | " z: 0.0\n" 42 | " w: 1.0\n" 43 | " }\n" 44 | "}\n" 45 | "" 46 | position { 47 | x: 0.0 48 | y: 6.7 49 | z: 10.5 50 | } 51 | rotation { 52 | x: -0.190809 53 | y: 0.0 54 | z: 0.0 55 | w: 0.98162717 56 | } 57 | scale3 { 58 | x: 1.0 59 | y: 1.0 60 | z: 1.0 61 | } 62 | } 63 | embedded_instances { 64 | id: "CupO" 65 | data: "components {\n" 66 | " id: \"CoffeeCupO\"\n" 67 | " component: \"/assets/animations/CoffeeCupO.model\"\n" 68 | " position {\n" 69 | " x: 0.0\n" 70 | " y: 0.0\n" 71 | " z: 0.0\n" 72 | " }\n" 73 | " rotation {\n" 74 | " x: 0.0\n" 75 | " y: 0.0\n" 76 | " z: 0.0\n" 77 | " w: 1.0\n" 78 | " }\n" 79 | " property_decls {\n" 80 | " }\n" 81 | "}\n" 82 | "" 83 | position { 84 | x: 0.0 85 | y: 0.0 86 | z: 0.0 87 | } 88 | rotation { 89 | x: 0.0 90 | y: 0.0 91 | z: 0.0 92 | w: 1.0 93 | } 94 | scale3 { 95 | x: 1.0 96 | y: 1.0 97 | z: 1.0 98 | } 99 | } 100 | embedded_instances { 101 | id: "gui" 102 | data: "components {\n" 103 | " id: \"buttons\"\n" 104 | " component: \"/assets/buttons.gui\"\n" 105 | " position {\n" 106 | " x: 0.0\n" 107 | " y: 0.0\n" 108 | " z: 0.0\n" 109 | " }\n" 110 | " rotation {\n" 111 | " x: 0.0\n" 112 | " y: 0.0\n" 113 | " z: 0.0\n" 114 | " w: 1.0\n" 115 | " }\n" 116 | " property_decls {\n" 117 | " }\n" 118 | "}\n" 119 | "" 120 | position { 121 | x: 0.0 122 | y: 0.0 123 | z: 0.0 124 | } 125 | rotation { 126 | x: 0.0 127 | y: 0.0 128 | z: 0.0 129 | w: 1.0 130 | } 131 | scale3 { 132 | x: 1.0 133 | y: 1.0 134 | z: 1.0 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /render/Basic3D.render: -------------------------------------------------------------------------------- 1 | script: "/render/Basic3D.render_script" 2 | -------------------------------------------------------------------------------- /render/Basic3D.render_script: -------------------------------------------------------------------------------- 1 | -- 2 | -- projection that centers content with maintained aspect ratio and optional zoom 3 | -- 4 | local function fixed_projection(near, far, zoom) 5 | local projected_width = render.get_window_width() / (zoom or 1) 6 | local projected_height = render.get_window_height() / (zoom or 1) 7 | local xoffset = -(projected_width - render.get_width()) / 2 8 | local yoffset = -(projected_height - render.get_height()) / 2 9 | return vmath.matrix4_orthographic(xoffset, xoffset + projected_width, yoffset, yoffset + projected_height, near, far) 10 | end 11 | -- 12 | -- projection that centers and fits content with maintained aspect ratio 13 | -- 14 | local function fixed_fit_projection(near, far) 15 | local width = render.get_width() 16 | local height = render.get_height() 17 | local window_width = render.get_window_width() 18 | local window_height = render.get_window_height() 19 | local zoom = math.min(window_width / width, window_height / height) 20 | return fixed_projection(near, far, zoom) 21 | end 22 | -- 23 | -- projection that stretches content 24 | -- 25 | local function stretch_projection(near, far) 26 | return vmath.matrix4_orthographic(0, render.get_width(), 0, render.get_height(), near, far) 27 | end 28 | 29 | local function get_projection(self) 30 | return self.projection_fn(self.near, self.far, self.zoom) 31 | end 32 | 33 | function init(self) 34 | self.tile_pred = render.predicate({"tile"}) 35 | self.gui_pred = render.predicate({"gui"}) 36 | self.particle_pred = render.predicate({"particle"}) 37 | self.model_pred = render.predicate({"model"}) 38 | 39 | self.clear_color = vmath.vector4(0, 0, 0, 0) 40 | self.clear_color.x = sys.get_config("render.clear_color_red", 0) 41 | self.clear_color.y = sys.get_config("render.clear_color_green", 0) 42 | self.clear_color.z = sys.get_config("render.clear_color_blue", 0) 43 | self.clear_color.w = sys.get_config("render.clear_color_alpha", 0) 44 | 45 | self.view = vmath.matrix4() 46 | 47 | self.near = -1 48 | self.far = 1 49 | self.projection_fn = stretch_projection 50 | end 51 | 52 | function update(self) 53 | local window_width = render.get_window_width() 54 | local window_height = render.get_window_height() 55 | if window_width == 0 or window_height == 0 then 56 | return 57 | end 58 | local proj = get_projection(self) 59 | local frustum_component = proj * self.view 60 | 61 | render.set_depth_mask(true) 62 | render.set_stencil_mask(0xff) 63 | render.clear({[render.BUFFER_COLOR_BIT] = self.clear_color, [render.BUFFER_DEPTH_BIT] = 1, [render.BUFFER_STENCIL_BIT] = 0}) 64 | 65 | render.set_viewport(0, 0, window_width, window_height) 66 | render.set_view(self.view) 67 | render.set_projection(proj) 68 | 69 | -- render models 70 | -- 71 | render.set_blend_func(render.BLEND_SRC_ALPHA, render.BLEND_ONE_MINUS_SRC_ALPHA) 72 | render.enable_state(render.STATE_BLEND) 73 | render.enable_state(render.STATE_STENCIL_TEST) 74 | render.enable_state(render.STATE_DEPTH_TEST) 75 | render.set_depth_mask(true) 76 | render.draw(self.model_pred, {frustum = frustum_component}) 77 | render.set_depth_mask(false) 78 | render.disable_state(render.STATE_DEPTH_TEST) 79 | render.disable_state(render.STATE_STENCIL_TEST) 80 | 81 | -- render sprites, label, particles 82 | -- 83 | 84 | render.enable_state(render.STATE_DEPTH_TEST) 85 | render.enable_state(render.STATE_STENCIL_TEST) 86 | render.draw(self.tile_pred, {frustum = frustum_component}) 87 | render.draw(self.particle_pred, {frustum = frustum_component}) 88 | render.disable_state(render.STATE_STENCIL_TEST) 89 | render.disable_state(render.STATE_DEPTH_TEST) 90 | 91 | -- debug 92 | render.draw_debug3d() 93 | 94 | -- render GUI 95 | -- 96 | local view_gui = vmath.matrix4() 97 | local proj_gui = vmath.matrix4_orthographic(0, window_width, 0, window_height, -1, 1) 98 | local frustum_gui = proj_gui * view_gui 99 | 100 | render.set_view(view_gui) 101 | render.set_projection(proj_gui) 102 | 103 | render.enable_state(render.STATE_STENCIL_TEST) 104 | render.draw(self.gui_pred, {frustum = frustum_gui}) 105 | render.disable_state(render.STATE_STENCIL_TEST) 106 | render.disable_state(render.STATE_BLEND) 107 | end 108 | 109 | function on_message(self, message_id, message) 110 | if message_id == hash("clear_color") then 111 | self.clear_color = message.color 112 | elseif message_id == hash("set_view_projection") then 113 | self.view = message.view 114 | self.projection = message.projection 115 | elseif message_id == hash("use_camera_projection") then 116 | self.projection_fn = function() return self.projection or vmath.matrix4() end 117 | elseif message_id == hash("use_stretch_projection") then 118 | self.near = message.near or -1 119 | self.far = message.far or 1 120 | self.projection_fn = stretch_projection 121 | elseif message_id == hash("use_fixed_projection") then 122 | self.near = message.near or -1 123 | self.far = message.far or 1 124 | self.zoom = message.zoom or 1 125 | self.projection_fn = fixed_projection 126 | elseif message_id == hash("use_fixed_fit_projection") then 127 | self.near = message.near or -1 128 | self.far = message.far or 1 129 | self.projection_fn = fixed_fit_projection 130 | end 131 | end 132 | --------------------------------------------------------------------------------