ADDED: Complete support for M3D animations! #2648

This commit is contained in:
Ray 2022-09-01 20:46:06 +02:00
parent 64cca24526
commit fb1037a241
7 changed files with 263 additions and 194 deletions

View file

@ -143,12 +143,13 @@ Examples using raylib models functionality, including models loading/generation
| 89 | [models_loading](models/models_loading.c) | <img src="models/models_loading.png" alt="models_loading" width="80"> | ⭐️☆☆☆ | 2.5 | **4.0** | [Ray](https://github.com/raysan5) | | 89 | [models_loading](models/models_loading.c) | <img src="models/models_loading.png" alt="models_loading" width="80"> | ⭐️☆☆☆ | 2.5 | **4.0** | [Ray](https://github.com/raysan5) |
| 90 | [models_loading_gltf](models/models_loading_gltf.c) | <img src="models/models_loading_gltf.png" alt="models_loading_gltf" width="80"> | ⭐️☆☆☆ | 3.7 | **4.2** | [Ray](https://github.com/raysan5) | | 90 | [models_loading_gltf](models/models_loading_gltf.c) | <img src="models/models_loading_gltf.png" alt="models_loading_gltf" width="80"> | ⭐️☆☆☆ | 3.7 | **4.2** | [Ray](https://github.com/raysan5) |
| 91 | [models_loading_vox](models/models_loading_vox.c) | <img src="models/models_loading_vox.png" alt="models_loading_vox" width="80"> | ⭐️☆☆☆ | **4.0** | **4.0** | [Johann Nadalutti](https://github.com/procfxgen) | | 91 | [models_loading_vox](models/models_loading_vox.c) | <img src="models/models_loading_vox.png" alt="models_loading_vox" width="80"> | ⭐️☆☆☆ | **4.0** | **4.0** | [Johann Nadalutti](https://github.com/procfxgen) |
| 92 | [models_orthographic_projection](models/models_orthographic_projection.c) | <img src="models/models_orthographic_projection.png" alt="models_orthographic_projection" width="80"> | ⭐️☆☆☆ | 2.0 | 3.7 | [Max Danielsson](https://github.com/autious) | | 92 | [models_loading_m3d](models/models_loading_m3d.c) | <img src="models/models_loading_m3d.png" alt="models_loading_m3d" width="80"> | ⭐️☆☆☆ | **4.2** | **4.2** | [bzt](https://bztsrc.gitlab.io/model3d) |
| 93 | [models_rlgl_solar_system](models/models_rlgl_solar_system.c) | <img src="models/models_rlgl_solar_system.png" alt="models_rlgl_solar_system" width="80"> | ⭐️⭐️⭐️⭐️ | 2.5 | **4.0** | [Ray](https://github.com/raysan5) | | 93 | [models_orthographic_projection](models/models_orthographic_projection.c) | <img src="models/models_orthographic_projection.png" alt="models_orthographic_projection" width="80"> | ⭐️☆☆☆ | 2.0 | 3.7 | [Max Danielsson](https://github.com/autious) |
| 94 | [models_yaw_pitch_roll](models/models_yaw_pitch_roll.c) | <img src="models/models_yaw_pitch_roll.png" alt="models_yaw_pitch_roll" width="80"> | ⭐️⭐️☆☆ | 1.8 | **4.0** | [Berni](https://github.com/Berni8k) | | 94 | [models_rlgl_solar_system](models/models_rlgl_solar_system.c) | <img src="models/models_rlgl_solar_system.png" alt="models_rlgl_solar_system" width="80"> | ⭐️⭐️⭐️⭐️ | 2.5 | **4.0** | [Ray](https://github.com/raysan5) |
| 95 | [models_waving_cubes](models/models_waving_cubes.c) | <img src="models/models_waving_cubes.png" alt="models_waving_cubes" width="80"> | ⭐️⭐️⭐️☆ | 2.5 | 3.7 | [codecat](https://github.com/codecat) | | 95 | [models_yaw_pitch_roll](models/models_yaw_pitch_roll.c) | <img src="models/models_yaw_pitch_roll.png" alt="models_yaw_pitch_roll" width="80"> | ⭐️⭐️☆☆ | 1.8 | **4.0** | [Berni](https://github.com/Berni8k) |
| 96 | [models_heightmap](models/models_heightmap.c) | <img src="models/models_heightmap.png" alt="models_heightmap" width="80"> | ⭐️☆☆☆ | 1.8 | 3.5 | [Ray](https://github.com/raysan5) | | 96 | [models_waving_cubes](models/models_waving_cubes.c) | <img src="models/models_waving_cubes.png" alt="models_waving_cubes" width="80"> | ⭐️⭐️⭐️☆ | 2.5 | 3.7 | [codecat](https://github.com/codecat) |
| 97 | [models_skybox](models/models_skybox.c) | <img src="models/models_skybox.png" alt="models_skybox" width="80"> | ⭐️⭐️☆☆ | 1.8 | **4.0** | [Ray](https://github.com/raysan5) | | 97 | [models_heightmap](models/models_heightmap.c) | <img src="models/models_heightmap.png" alt="models_heightmap" width="80"> | ⭐️☆☆☆ | 1.8 | 3.5 | [Ray](https://github.com/raysan5) |
| 98 | [models_skybox](models/models_skybox.c) | <img src="models/models_skybox.png" alt="models_skybox" width="80"> | ⭐️⭐️☆☆ | 1.8 | **4.0** | [Ray](https://github.com/raysan5) |
### category: shaders ### category: shaders
@ -156,24 +157,24 @@ Examples using raylib shaders functionality, including shaders loading, paramete
| ## | example | image | difficulty<br>level | version<br>created | last version<br>updated | original<br>developer | | ## | example | image | difficulty<br>level | version<br>created | last version<br>updated | original<br>developer |
|----|----------|--------|:-------------------:|:------------------:|:------------------:|:----------| |----|----------|--------|:-------------------:|:------------------:|:------------------:|:----------|
| 98 | [shaders_basic_lighting](shaders/shaders_basic_lighting.c) | <img src="shaders/shaders_basic_lighting.png" alt="shaders_basic_lighting" width="80"> | ⭐️⭐️⭐️⭐️ | 3.0 | **4.2** | [Chris Camacho](https://github.com/codifies) | | 99 | [shaders_basic_lighting](shaders/shaders_basic_lighting.c) | <img src="shaders/shaders_basic_lighting.png" alt="shaders_basic_lighting" width="80"> | ⭐️⭐️⭐️⭐️ | 3.0 | **4.2** | [Chris Camacho](https://github.com/codifies) |
| 99 | [shaders_model_shader](shaders/shaders_model_shader.c) | <img src="shaders/shaders_model_shader.png" alt="shaders_model_shader" width="80"> | ⭐️⭐️☆☆ | 1.3 | 3.7 | [Ray](https://github.com/raysan5) | | 100 | [shaders_model_shader](shaders/shaders_model_shader.c) | <img src="shaders/shaders_model_shader.png" alt="shaders_model_shader" width="80"> | ⭐️⭐️☆☆ | 1.3 | 3.7 | [Ray](https://github.com/raysan5) |
| 100 | [shaders_shapes_textures](shaders/shaders_shapes_textures.c) | <img src="shaders/shaders_shapes_textures.png" alt="shaders_shapes_textures" width="80"> | ⭐️⭐️☆☆ | 1.7 | 3.7 | [Ray](https://github.com/raysan5) | | 101 | [shaders_shapes_textures](shaders/shaders_shapes_textures.c) | <img src="shaders/shaders_shapes_textures.png" alt="shaders_shapes_textures" width="80"> | ⭐️⭐️☆☆ | 1.7 | 3.7 | [Ray](https://github.com/raysan5) |
| 101 | [shaders_custom_uniform](shaders/shaders_custom_uniform.c) | <img src="shaders/shaders_custom_uniform.png" alt="shaders_custom_uniform" width="80"> | ⭐️⭐️☆☆ | 1.3 | **4.0** | [Ray](https://github.com/raysan5) | | 102 | [shaders_custom_uniform](shaders/shaders_custom_uniform.c) | <img src="shaders/shaders_custom_uniform.png" alt="shaders_custom_uniform" width="80"> | ⭐️⭐️☆☆ | 1.3 | **4.0** | [Ray](https://github.com/raysan5) |
| 102 | [shaders_postprocessing](shaders/shaders_postprocessing.c) | <img src="shaders/shaders_postprocessing.png" alt="shaders_postprocessing" width="80"> | ⭐️⭐️⭐️☆ | 1.3 | **4.0** | [Ray](https://github.com/raysan5) | | 103 | [shaders_postprocessing](shaders/shaders_postprocessing.c) | <img src="shaders/shaders_postprocessing.png" alt="shaders_postprocessing" width="80"> | ⭐️⭐️⭐️☆ | 1.3 | **4.0** | [Ray](https://github.com/raysan5) |
| 103 | [shaders_palette_switch](shaders/shaders_palette_switch.c) | <img src="shaders/shaders_palette_switch.png" alt="shaders_palette_switch" width="80"> | ⭐️⭐️⭐️☆ | 2.5 | 3.7 | [Marco Lizza](https://github.com/MarcoLizza) | | 104 | [shaders_palette_switch](shaders/shaders_palette_switch.c) | <img src="shaders/shaders_palette_switch.png" alt="shaders_palette_switch" width="80"> | ⭐️⭐️⭐️☆ | 2.5 | 3.7 | [Marco Lizza](https://github.com/MarcoLizza) |
| 104 | [shaders_raymarching](shaders/shaders_raymarching.c) | <img src="shaders/shaders_raymarching.png" alt="shaders_raymarching" width="80"> | ⭐️⭐️⭐️⭐️ | 2.0 | **4.2** | [Ray](https://github.com/raysan5) | | 105 | [shaders_raymarching](shaders/shaders_raymarching.c) | <img src="shaders/shaders_raymarching.png" alt="shaders_raymarching" width="80"> | ⭐️⭐️⭐️⭐️ | 2.0 | **4.2** | [Ray](https://github.com/raysan5) |
| 105 | [shaders_texture_drawing](shaders/shaders_texture_drawing.c) | <img src="shaders/shaders_texture_drawing.png" alt="shaders_texture_drawing" width="80"> | ⭐️⭐️☆☆ | 2.0 | 3.7 | [Michał Ciesielski](https://github.com/) | | 106 | [shaders_texture_drawing](shaders/shaders_texture_drawing.c) | <img src="shaders/shaders_texture_drawing.png" alt="shaders_texture_drawing" width="80"> | ⭐️⭐️☆☆ | 2.0 | 3.7 | [Michał Ciesielski](https://github.com/) |
| 106 | [shaders_texture_outline](shaders/shaders_texture_outline.c) | <img src="shaders/shaders_texture_outline.png" alt="shaders_texture_outline" width="80"> | ⭐️⭐️⭐️☆ | **4.0** | **4.0** | [Samuel Skiff](https://github.com/GoldenThumbs) | | 107 | [shaders_texture_outline](shaders/shaders_texture_outline.c) | <img src="shaders/shaders_texture_outline.png" alt="shaders_texture_outline" width="80"> | ⭐️⭐️⭐️☆ | **4.0** | **4.0** | [Samuel Skiff](https://github.com/GoldenThumbs) |
| 107 | [shaders_texture_waves](shaders/shaders_texture_waves.c) | <img src="shaders/shaders_texture_waves.png" alt="shaders_texture_waves" width="80"> | ⭐️⭐️☆☆ | 2.5 | 3.7 | [Anata](https://github.com/anatagawa) | | 108 | [shaders_texture_waves](shaders/shaders_texture_waves.c) | <img src="shaders/shaders_texture_waves.png" alt="shaders_texture_waves" width="80"> | ⭐️⭐️☆☆ | 2.5 | 3.7 | [Anata](https://github.com/anatagawa) |
| 108 | [shaders_julia_set](shaders/shaders_julia_set.c) | <img src="shaders/shaders_julia_set.png" alt="shaders_julia_set" width="80"> | ⭐️⭐️⭐️☆ | 2.5 | **4.0** | [eggmund](https://github.com/eggmund) | | 109 | [shaders_julia_set](shaders/shaders_julia_set.c) | <img src="shaders/shaders_julia_set.png" alt="shaders_julia_set" width="80"> | ⭐️⭐️⭐️☆ | 2.5 | **4.0** | [eggmund](https://github.com/eggmund) |
| 109 | [shaders_eratosthenes](shaders/shaders_eratosthenes.c) | <img src="shaders/shaders_eratosthenes.png" alt="shaders_eratosthenes" width="80"> | ⭐️⭐️⭐️☆ | 2.5 | **4.0** | [ProfJski](https://github.com/ProfJski) | | 110 | [shaders_eratosthenes](shaders/shaders_eratosthenes.c) | <img src="shaders/shaders_eratosthenes.png" alt="shaders_eratosthenes" width="80"> | ⭐️⭐️⭐️☆ | 2.5 | **4.0** | [ProfJski](https://github.com/ProfJski) |
| 110 | [shaders_fog](shaders/shaders_fog.c) | <img src="shaders/shaders_fog.png" alt="shaders_fog" width="80"> | ⭐️⭐️⭐️☆ | 2.5 | 3.7 | [Chris Camacho](https://github.com/codifies) | | 111 | [shaders_fog](shaders/shaders_fog.c) | <img src="shaders/shaders_fog.png" alt="shaders_fog" width="80"> | ⭐️⭐️⭐️☆ | 2.5 | 3.7 | [Chris Camacho](https://github.com/codifies) |
| 111 | [shaders_simple_mask](shaders/shaders_simple_mask.c) | <img src="shaders/shaders_simple_mask.png" alt="shaders_simple_mask" width="80"> | ⭐️⭐️☆☆ | 2.5 | 3.7 | [Chris Camacho](https://github.com/codifies) | | 112 | [shaders_simple_mask](shaders/shaders_simple_mask.c) | <img src="shaders/shaders_simple_mask.png" alt="shaders_simple_mask" width="80"> | ⭐️⭐️☆☆ | 2.5 | 3.7 | [Chris Camacho](https://github.com/codifies) |
| 112 | [shaders_hot_reloading](shaders/shaders_hot_reloading.c) | <img src="shaders/shaders_hot_reloading.png" alt="shaders_hot_reloading" width="80"> | ⭐️⭐️⭐️☆ | 3.0 | 3.5 | [Ray](https://github.com/raysan5) | | 113 | [shaders_hot_reloading](shaders/shaders_hot_reloading.c) | <img src="shaders/shaders_hot_reloading.png" alt="shaders_hot_reloading" width="80"> | ⭐️⭐️⭐️☆ | 3.0 | 3.5 | [Ray](https://github.com/raysan5) |
| 113 | [shaders_mesh_instancing](shaders/shaders_mesh_instancing.c) | <img src="shaders/shaders_mesh_instancing.png" alt="shaders_mesh_instancing" width="80"> | ⭐️⭐️⭐️⭐️ | 3.7 | **4.2** | [seanpringle](https://github.com/seanpringle) | | 114 | [shaders_mesh_instancing](shaders/shaders_mesh_instancing.c) | <img src="shaders/shaders_mesh_instancing.png" alt="shaders_mesh_instancing" width="80"> | ⭐️⭐️⭐️⭐️ | 3.7 | **4.2** | [seanpringle](https://github.com/seanpringle) |
| 114 | [shaders_multi_sample2d](shaders/shaders_multi_sample2d.c) | <img src="shaders/shaders_multi_sample2d.png" alt="shaders_multi_sample2d" width="80"> | ⭐️⭐️☆☆ | 3.5 | 3.5 | [Ray](https://github.com/raysan5) | | 115 | [shaders_multi_sample2d](shaders/shaders_multi_sample2d.c) | <img src="shaders/shaders_multi_sample2d.png" alt="shaders_multi_sample2d" width="80"> | ⭐️⭐️☆☆ | 3.5 | 3.5 | [Ray](https://github.com/raysan5) |
| 115 | [shaders_spotlight](shaders/shaders_spotlight.c) | <img src="shaders/shaders_spotlight.png" alt="shaders_spotlight" width="80"> | ⭐️⭐️☆☆ | 2.5 | 3.7 | [Chris Camacho](https://github.com/codifies) | | 116 | [shaders_spotlight](shaders/shaders_spotlight.c) | <img src="shaders/shaders_spotlight.png" alt="shaders_spotlight" width="80"> | ⭐️⭐️☆☆ | 2.5 | 3.7 | [Chris Camacho](https://github.com/codifies) |
### category: audio ### category: audio
@ -181,11 +182,11 @@ Examples using raylib audio functionality, including sound/music loading and pla
| ## | example | image | difficulty<br>level | version<br>created | last version<br>updated | original<br>developer | | ## | example | image | difficulty<br>level | version<br>created | last version<br>updated | original<br>developer |
|----|----------|--------|:-------------------:|:------------------:|:------------------:|:----------| |----|----------|--------|:-------------------:|:------------------:|:------------------:|:----------|
| 116 | [audio_module_playing](audio/audio_module_playing.c) | <img src="audio/audio_module_playing.png" alt="audio_module_playing" width="80"> | ⭐️☆☆☆ | 1.5 | 3.5 | [Ray](https://github.com/raysan5) | | 117 | [audio_module_playing](audio/audio_module_playing.c) | <img src="audio/audio_module_playing.png" alt="audio_module_playing" width="80"> | ⭐️☆☆☆ | 1.5 | 3.5 | [Ray](https://github.com/raysan5) |
| 117 | [audio_music_stream](audio/audio_music_stream.c) | <img src="audio/audio_music_stream.png" alt="audio_music_stream" width="80"> | ⭐️☆☆☆ | 1.3 | **4.2** | [Ray](https://github.com/raysan5) | | 118 | [audio_music_stream](audio/audio_music_stream.c) | <img src="audio/audio_music_stream.png" alt="audio_music_stream" width="80"> | ⭐️☆☆☆ | 1.3 | **4.2** | [Ray](https://github.com/raysan5) |
| 118 | [audio_raw_stream](audio/audio_raw_stream.c) | <img src="audio/audio_raw_stream.png" alt="audio_raw_stream" width="80"> | ⭐️⭐️⭐️☆ | 1.6 | **4.2** | [Ray](https://github.com/raysan5) | | 119 | [audio_raw_stream](audio/audio_raw_stream.c) | <img src="audio/audio_raw_stream.png" alt="audio_raw_stream" width="80"> | ⭐️⭐️⭐️☆ | 1.6 | **4.2** | [Ray](https://github.com/raysan5) |
| 119 | [audio_sound_loading](audio/audio_sound_loading.c) | <img src="audio/audio_sound_loading.png" alt="audio_sound_loading" width="80"> | ⭐️☆☆☆ | 1.1 | 3.5 | [Ray](https://github.com/raysan5) | | 120 | [audio_sound_loading](audio/audio_sound_loading.c) | <img src="audio/audio_sound_loading.png" alt="audio_sound_loading" width="80"> | ⭐️☆☆☆ | 1.1 | 3.5 | [Ray](https://github.com/raysan5) |
| 120 | [audio_multichannel_sound](audio/audio_multichannel_sound.c) | <img src="audio/audio_multichannel_sound.png" alt="audio_multichannel_sound" width="80"> | ⭐️☆☆☆ | 3.0 | 3.5 | [Chris Camacho](https://github.com/codifies) | | 121 | [audio_multichannel_sound](audio/audio_multichannel_sound.c) | <img src="audio/audio_multichannel_sound.png" alt="audio_multichannel_sound" width="80"> | ⭐️☆☆☆ | 3.0 | 3.5 | [Chris Camacho](https://github.com/codifies) |
### category: others ### category: others
@ -193,11 +194,11 @@ Examples showing raylib misc functionality that does not fit in other categories
| ## | example | image | difficulty<br>level | version<br>created | last version<br>updated | original<br>developer | | ## | example | image | difficulty<br>level | version<br>created | last version<br>updated | original<br>developer |
|----|----------|--------|:-------------------:|:------------------:|:------------------:|:----------| |----|----------|--------|:-------------------:|:------------------:|:------------------:|:----------|
| 121 | [rlgl_standalone](others/rlgl_standalone.c) | <img src="others/rlgl_standalone.png" alt="rlgl_standalone" width="80"> | ⭐️⭐️⭐️⭐️ | 1.6 | **4.0** | [Ray](https://github.com/raysan5) | | 122 | [rlgl_standalone](others/rlgl_standalone.c) | <img src="others/rlgl_standalone.png" alt="rlgl_standalone" width="80"> | ⭐️⭐️⭐️⭐️ | 1.6 | **4.0** | [Ray](https://github.com/raysan5) |
| 122 | [rlgl_compute_shader](others/rlgl_compute_shader.c) | <img src="others/rlgl_compute_shader.png" alt="rlgl_compute_shader" width="80"> | ⭐️⭐️⭐️⭐️ | **4.0** | **4.0** | [Teddy Astie](https://github.com/tsnake41) | | 123 | [rlgl_compute_shader](others/rlgl_compute_shader.c) | <img src="others/rlgl_compute_shader.png" alt="rlgl_compute_shader" width="80"> | ⭐️⭐️⭐️⭐️ | **4.0** | **4.0** | [Teddy Astie](https://github.com/tsnake41) |
| 123 | [easings_testbed](others/easings_testbed.c) | <img src="others/easings_testbed.png" alt="easings_testbed" width="80"> | ⭐️⭐️⭐️☆ | 3.0 | 3.0 | [Juan Miguel López](https://github.com/flashback-fx) | | 124 | [easings_testbed](others/easings_testbed.c) | <img src="others/easings_testbed.png" alt="easings_testbed" width="80"> | ⭐️⭐️⭐️☆ | 3.0 | 3.0 | [Juan Miguel López](https://github.com/flashback-fx) |
| 124 | [raylib_opengl_interop](others/raylib_opengl_interop.c) | <img src="others/raylib_opengl_interop.png" alt="raylib_opengl_interop" width="80"> | ⭐️⭐️⭐️⭐️ | **4.0** | **4.0** | [Stephan Soller](https://github.com/arkanis) | | 125 | [raylib_opengl_interop](others/raylib_opengl_interop.c) | <img src="others/raylib_opengl_interop.png" alt="raylib_opengl_interop" width="80"> | ⭐️⭐️⭐️⭐️ | **4.0** | **4.0** | [Stephan Soller](https://github.com/arkanis) |
| 125 | [embedded_files_loading](others/embedded_files_loading.c) | <img src="others/embedded_files_loading.png" alt="embedded_files_loading" width="80"> | ⭐️⭐️☆☆ | 3.5 | 3.5 | [Kristian Holmgren](https://github.com/defutura) | | 126 | [embedded_files_loading](others/embedded_files_loading.c) | <img src="others/embedded_files_loading.png" alt="embedded_files_loading" width="80"> | ⭐️⭐️☆☆ | 3.5 | 3.5 | [Kristian Holmgren](https://github.com/defutura) |
As always contributions are welcome, feel free to send new examples! Here it is an [examples template](examples_template.c) to start with! As always contributions are welcome, feel free to send new examples! Here it is an [examples template](examples_template.c) to start with!

View file

@ -1,60 +1,62 @@
/******************************************************************************************* /*******************************************************************************************
* *
* raylib [models] example - Load M3D model (with optional animations) and play them * raylib [models] example - Load models M3D
* *
* Example static mesh Suzanne from Blender * Example originally created with raylib 4.5-dev, last time updated with raylib 4.5-dev
* Example animated seagull model from Scorched 3D, licensed GPLv2 *
* Example contributed by bzt (@bztsrc) and reviewed by Ramon Santamaria (@raysan5)
*
* NOTES:
* - Model3D (M3D) fileformat specs: https://gitlab.com/bztsrc/model3d
* - Bender M3D exported: https://gitlab.com/bztsrc/model3d/-/tree/master/blender
* WARNING: Make sure to add "(action)" markers to the timeline if you want multiple animations.
*
* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
* BSD-like license that allows static linking with closed source software
* *
* Copyright (c) 2019-2022 Culacant (@culacant) and Ramon Santamaria (@raysan5)
* Copyright (c) 2022 bzt (@bztsrc) * Copyright (c) 2022 bzt (@bztsrc)
* *
********************************************************************************************
*
* NOTE: To export a model from blender, just use https://gitlab.com/bztsrc/model3d/-/tree/master/blender
* and make sure to add "(action)" markers to the timeline if you want multiple animations.
*
********************************************************************************************/ ********************************************************************************************/
#include "raylib.h" #include "raylib.h"
#include <stdlib.h>
#include <stdio.h>
//------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------
// Program main entry point // Program main entry point
//------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------
int main(int argc, char **argv) int main(void)
{ {
char *model_fn = argc > 1 ? argv[1] : "resources/models/m3d/seagull.m3d";
// Initialization // Initialization
//-------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------
const int screenWidth = 800; const int screenWidth = 800;
const int screenHeight = 450; const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [models] example - M3D model"); InitWindow(screenWidth, screenHeight, "raylib [models] example - M3D model loading");
// Define the camera to look into our 3d world // Define the camera to look into our 3d world
Camera camera = { 0 }; Camera camera = { 0 };
camera.position = (Vector3){ 10.0f, 10.0f, 10.0f }; // Camera position camera.position = (Vector3){ 1.5f, 1.5f, 1.5f }; // Camera position
camera.target = (Vector3){ 0.0f, 0.0f, 0.0f }; // Camera looking at point camera.target = (Vector3){ 0.0f, 0.4f, 0.0f }; // Camera looking at point
camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; // Camera up vector (rotation towards target) camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; // Camera up vector (rotation towards target)
camera.fovy = 45.0f; // Camera field-of-view Y camera.fovy = 45.0f; // Camera field-of-view Y
camera.projection = CAMERA_PERSPECTIVE; // Camera mode type camera.projection = CAMERA_PERSPECTIVE; // Camera mode type
Vector3 position = { 0.0f, 0.0f, 0.0f }; // Set model position Vector3 position = { 0.0f, 0.0f, 0.0f }; // Set model position
// Load model char modelFileName[128] = "resources/models/m3d/CesiumMan.m3d";
Model model = LoadModel(model_fn); // Load the animated model mesh and basic data bool drawMesh = 1;
bool drawSkeleton = 1;
bool animPlaying = false; // Store anim state, what to draw
// Load animation data // Load model
unsigned int animsCount = 0, animsSkel = 1, animsMesh = 1; Model model = LoadModel(modelFileName); // Load the bind-pose model mesh and basic data
ModelAnimation *anims = LoadModelAnimations(model_fn, &animsCount);
// Load animations
unsigned int animsCount = 0;
int animFrameCounter = 0, animId = 0; int animFrameCounter = 0, animId = 0;
ModelAnimation *anims = LoadModelAnimations(modelFileName, &animsCount); // Load skeletal animation data
SetCameraMode(camera, CAMERA_FREE); // Set free camera mode SetCameraMode(camera, CAMERA_FREE); // Set free camera mode
SetTargetFPS(60); // Set our game to run at 60 frames-per-second SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//-------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------
// Main game loop // Main game loop
@ -64,34 +66,36 @@ int main(int argc, char **argv)
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
UpdateCamera(&camera); UpdateCamera(&camera);
// Play animation when spacebar is held down
if (animsCount) if (animsCount)
{ {
if (IsKeyDown(KEY_SPACE)) // Play animation when spacebar is held down (or step one frame with N)
if (IsKeyDown(KEY_SPACE) || IsKeyPressed(KEY_N))
{ {
animFrameCounter++; animFrameCounter++;
UpdateModelAnimation(model, anims[animId], animFrameCounter);
if (animFrameCounter >= anims[animId].frameCount) animFrameCounter = 0; if (animFrameCounter >= anims[animId].frameCount) animFrameCounter = 0;
//printf("anim %u, frame %u / %u\n",animId,animFrameCounter,anims[animId].frameCount);
} UpdateModelAnimation(model, anims[animId], animFrameCounter);
if (IsKeyDown(KEY_S)) animPlaying = true;
{
animsSkel ^= 1;
}
if (IsKeyDown(KEY_M))
{
animsMesh ^= 1;
} }
// Select animation on mouse click // Select animation by pressing A
if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) if (IsKeyPressed(KEY_A))
{ {
animFrameCounter = 0; animFrameCounter = 0;
animId++; animId++;
if (animId >= animsCount) animId = 0; if (animId >= animsCount) animId = 0;
UpdateModelAnimation(model, anims[animId], 0); UpdateModelAnimation(model, anims[animId], 0);
animPlaying = true;
} }
} }
// Toggle skeleton drawing
if (IsKeyPressed(KEY_S)) drawSkeleton ^= 1;
// Toggle mesh drawing
if (IsKeyPressed(KEY_M)) drawMesh ^= 1;
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
// Draw // Draw
@ -102,25 +106,53 @@ int main(int argc, char **argv)
BeginMode3D(camera); BeginMode3D(camera);
if (animsMesh) // Draw 3d model with texture
DrawModel(model, position, 1.0f, WHITE); // Draw 3d model with texture if (drawMesh) DrawModel(model, position, 1.0f, WHITE);
if (anims && animsSkel) // Draw the animated skeleton
for (int i = 0; i < model.boneCount; i++) if (drawSkeleton)
{ {
// Loop to (boneCount - 1) because the last one is a special "no bone" bone,
// needed to workaround buggy models
// without a -1, we would always draw a cube at the origin
for (int i = 0; i < model.boneCount - 1; i++)
{
// By default the model is loaded in bind-pose by LoadModel().
// But if UpdateModelAnimation() has been called at least once
// then the model is already in animation pose, so we need the animated skeleton
if (!animPlaying || !animsCount)
{
// Display the bind-pose skeleton
DrawCube(model.bindPose[i].translation, 0.04f, 0.04f, 0.04f, RED);
if (model.bones[i].parent >= 0)
{
DrawLine3D(model.bindPose[i].translation,
model.bindPose[model.bones[i].parent].translation, RED);
}
}
else
{
// Display the frame-pose skeleton
DrawCube(anims[animId].framePoses[animFrameCounter][i].translation, 0.05f, 0.05f, 0.05f, RED); DrawCube(anims[animId].framePoses[animFrameCounter][i].translation, 0.05f, 0.05f, 0.05f, RED);
if (anims[animId].bones[i].parent >= 0) if (anims[animId].bones[i].parent >= 0)
{
DrawLine3D(anims[animId].framePoses[animFrameCounter][i].translation, DrawLine3D(anims[animId].framePoses[animFrameCounter][i].translation,
anims[animId].framePoses[animFrameCounter][anims[animId].bones[i].parent].translation, RED); anims[animId].framePoses[animFrameCounter][anims[animId].bones[i].parent].translation, RED);
} }
}
}
}
DrawGrid(10, 1.0f); // Draw a grid DrawGrid(10, 1.0f); // Draw a grid
EndMode3D(); EndMode3D();
DrawText("PRESS SPACE to PLAY MODEL ANIMATION", 10, GetScreenHeight() - 30, 10, MAROON); DrawText("PRESS SPACE to PLAY MODEL ANIMATION", 10, GetScreenHeight() - 60, 10, MAROON);
DrawText("MOUSE LEFT BUTTON to CYCLE THROUGH ANIMATIONS", 10, GetScreenHeight() - 20, 10, DARKGRAY); DrawText("PRESS A to CYCLE THROUGH ANIMATIONS", 10, GetScreenHeight() - 40, 10, DARKGRAY);
DrawText("(c) Seagull model by Scorched3D", screenWidth - 200, screenHeight - 20, 10, GRAY); DrawText("PRESS M to toggle MESH, S to toggle SKELETON DRAWING", 10, GetScreenHeight() - 20, 10, DARKGRAY);
DrawText("(c) CesiumMan model by KhronosGroup", GetScreenWidth() - 210, GetScreenHeight() - 20, 10, GRAY);
EndDrawing(); EndDrawing();
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
@ -130,8 +162,7 @@ int main(int argc, char **argv)
//-------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------
// Unload model animations data // Unload model animations data
for (unsigned int i = 0; i < animsCount; i++) UnloadModelAnimation(anims[i]); UnloadModelAnimations(anims, animsCount);
RL_FREE(anims);
UnloadModel(model); // Unload model UnloadModel(model); // Unload model

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 23 KiB

Before After
Before After

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -5264,10 +5264,13 @@ static Model LoadM3D(const char *fileName)
} }
// Add skin (vertex / bone weight pairs) // Add skin (vertex / bone weight pairs)
if (m3d->numbone && m3d->numskin) { if (m3d->numbone && m3d->numskin)
for (n = 0; n < 3; n++) { {
for (n = 0; n < 3; n++)
{
int skinid = m3d->vertex[m3d->face[i].vertex[n]].skinid; int skinid = m3d->vertex[m3d->face[i].vertex[n]].skinid;
// check if there's a skin for this mesh, should be, just failsafe
// Check if there is a skin for this mesh, should be, just failsafe
if (skinid != M3D_UNDEF && skinid < m3d->numskin) if (skinid != M3D_UNDEF && skinid < m3d->numskin)
{ {
for (j = 0; j < 4; j++) for (j = 0; j < 4; j++)
@ -5276,6 +5279,13 @@ static Model LoadM3D(const char *fileName)
model.meshes[k].boneWeights[l*12 + n*4 + j] = m3d->skin[skinid].weight[j]; model.meshes[k].boneWeights[l*12 + n*4 + j] = m3d->skin[skinid].weight[j];
} }
} }
else
{
// raylib does not handle boneless meshes with skeletal animations, so
// we put all vertices without a bone into a special "no bone" bone
model.meshes[k].boneIds[l * 12 + n * 4] = m3d->numbone;
model.meshes[k].boneWeights[l * 12 + n * 4] = 1.0f;
}
} }
} }
} }
@ -5354,9 +5364,10 @@ static Model LoadM3D(const char *fileName)
// Load bones // Load bones
if (m3d->numbone) if (m3d->numbone)
{ {
model.boneCount = m3d->numbone; model.boneCount = m3d->numbone + 1;
model.bones = RL_MALLOC(m3d->numbone*sizeof(BoneInfo)); model.bones = RL_CALLOC(model.boneCount, sizeof(BoneInfo));
model.bindPose = RL_MALLOC(m3d->numbone*sizeof(Transform)); model.bindPose = RL_CALLOC(model.boneCount, sizeof(Transform));
for (i = 0; i < m3d->numbone; i++) for (i = 0; i < m3d->numbone; i++)
{ {
model.bones[i].parent = m3d->bone[i].parent; model.bones[i].parent = m3d->bone[i].parent;
@ -5381,6 +5392,18 @@ static Model LoadM3D(const char *fileName)
model.bindPose[i].scale = Vector3Multiply(model.bindPose[i].scale, model.bindPose[model.bones[i].parent].scale); model.bindPose[i].scale = Vector3Multiply(model.bindPose[i].scale, model.bindPose[model.bones[i].parent].scale);
} }
} }
// Add a special "no bone" bone
model.bones[i].parent = -1;
strcpy(model.bones[i].name, "NO BONE");
model.bindPose[i].translation.x = 0.0f;
model.bindPose[i].translation.y = 0.0f;
model.bindPose[i].translation.z = 0.0f;
model.bindPose[i].rotation.x = 0.0f;
model.bindPose[i].rotation.y = 0.0f;
model.bindPose[i].rotation.z = 0.0f;
model.bindPose[i].rotation.w = 1.0f;
model.bindPose[i].scale.x = model.bindPose[i].scale.y = model.bindPose[i].scale.z = 1.0f;
} }
// Load bone-pose default mesh into animation vertices. These will be updated when UpdateModelAnimation gets // Load bone-pose default mesh into animation vertices. These will be updated when UpdateModelAnimation gets
@ -5440,8 +5463,8 @@ static ModelAnimation *LoadModelAnimationsM3D(const char *fileName, unsigned int
for (unsigned int a = 0; a < m3d->numaction; a++) for (unsigned int a = 0; a < m3d->numaction; a++)
{ {
animations[a].frameCount = m3d->action[a].durationmsec / M3D_ANIMDELAY; animations[a].frameCount = m3d->action[a].durationmsec / M3D_ANIMDELAY;
animations[a].boneCount = m3d->numbone; animations[a].boneCount = m3d->numbone + 1;
animations[a].bones = RL_MALLOC(m3d->numbone*sizeof(BoneInfo)); animations[a].bones = RL_MALLOC((m3d->numbone + 1)*sizeof(BoneInfo));
animations[a].framePoses = RL_MALLOC(animations[a].frameCount*sizeof(Transform *)); animations[a].framePoses = RL_MALLOC(animations[a].frameCount*sizeof(Transform *));
// strncpy(animations[a].name, m3d->action[a].name, sizeof(animations[a].name)); // strncpy(animations[a].name, m3d->action[a].name, sizeof(animations[a].name));
TRACELOG(LOG_INFO, "MODEL: [%s] animation #%i: %i msec, %i frames", fileName, a, m3d->action[a].durationmsec, animations[a].frameCount); TRACELOG(LOG_INFO, "MODEL: [%s] animation #%i: %i msec, %i frames", fileName, a, m3d->action[a].durationmsec, animations[a].frameCount);
@ -5452,11 +5475,15 @@ static ModelAnimation *LoadModelAnimationsM3D(const char *fileName, unsigned int
strncpy(animations[a].bones[i].name, m3d->bone[i].name, sizeof(animations[a].bones[i].name)); strncpy(animations[a].bones[i].name, m3d->bone[i].name, sizeof(animations[a].bones[i].name));
} }
// A special, never transformed "no bone" bone, used for boneless vertices
animations[a].bones[i].parent = -1;
strcpy(animations[a].bones[i].name, "NO BONE");
// M3D stores frames at arbitrary intervals with sparse skeletons. We need full skeletons at // M3D stores frames at arbitrary intervals with sparse skeletons. We need full skeletons at
// regular intervals, so let the M3D SDK do the heavy lifting and calculate interpolated bones // regular intervals, so let the M3D SDK do the heavy lifting and calculate interpolated bones
for (i = 0; i < animations[a].frameCount; i++) for (i = 0; i < animations[a].frameCount; i++)
{ {
animations[a].framePoses[i] = RL_MALLOC(m3d->numbone*sizeof(Transform)); animations[a].framePoses[i] = RL_MALLOC((m3d->numbone + 1)*sizeof(Transform));
m3db_t *pose = m3d_pose(m3d, a, i * M3D_ANIMDELAY); m3db_t *pose = m3d_pose(m3d, a, i * M3D_ANIMDELAY);
if (pose != NULL) if (pose != NULL)
@ -5482,6 +5509,16 @@ static ModelAnimation *LoadModelAnimationsM3D(const char *fileName, unsigned int
animations[a].framePoses[i][j].scale = Vector3Multiply(animations[a].framePoses[i][j].scale, animations[a].framePoses[i][animations[a].bones[j].parent].scale); animations[a].framePoses[i][j].scale = Vector3Multiply(animations[a].framePoses[i][j].scale, animations[a].framePoses[i][animations[a].bones[j].parent].scale);
} }
} }
// Default transform for the "no bone" bone
animations[a].framePoses[i][j].translation.x = 0.0f;
animations[a].framePoses[i][j].translation.y = 0.0f;
animations[a].framePoses[i][j].translation.z = 0.0f;
animations[a].framePoses[i][j].rotation.x = 0.0f;
animations[a].framePoses[i][j].rotation.y = 0.0f;
animations[a].framePoses[i][j].rotation.z = 0.0f;
animations[a].framePoses[i][j].rotation.w = 1.0f;
animations[a].framePoses[i][j].scale.x = animations[a].framePoses[i][j].scale.y = animations[a].framePoses[i][j].scale.z = 1.0f;
RL_FREE(pose); RL_FREE(pose);
} }
} }