Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make characters (player avatars) moddable #2942

Open
wants to merge 32 commits into
base: master
Choose a base branch
from

Conversation

ohlidalp
Copy link
Member

@ohlidalp ohlidalp commented Sep 19, 2022

Needs updated rorserver: RigsOfRods/ror-server#153

Present rorbot is 100% hardcoded which prevents artists like Vido from really improving him. And while at it, it makes sense to make him 100% moddable.

The classic rorbot is now a mod as well, and lives in /resources/default_character.zip. If no other mod is installed, game falls back to this one.

I've created a detailed tutorial for complete beginners: https://forum.rigsofrods.org/threads/beginner-tutorial-character-rorbot-mod-from-scratch-in-blender.3761/ - it will guide you through modelling, animating and exporting with Blender and blender2ogre.

Example character mod (from the above tutorial):
rorbotmod-tut1-rev1.zip
50B ROR simulation with poseutil UI

To use it, just place it anywhere in your mods directory and enable it using Settings UI/Gameplay tab.
50 ROR mainmenu Settings Selector

@ohlidalp
Copy link
Member Author

ohlidalp commented Sep 23, 2022

Regarding rorbot, I have these thoughts:

  • we want to fade animations in and out, like when running and stopping. This involves the Blend Weight param but possibly playback speed.
  • we need something like soundscripts for animations, they allow defining start/stop effects.
  • While at it, I might as well make rorbot a new type of mod, with a definition file, recognized by modcache and selectable with selector. Not much work, I did it all before, see my 'addon_API' branch in my repo.
  • Since we now have a navigation mesh system with built in ability to guide crowds of of point figures like rorbot, this all nicely fits together
  • see also MakeHuman project.

And that was only the part on visuals! Further on the simulation like movement:

  • historically it was simply "set pos, rot, animation type and time of the entity" directly from main loop where pos/rot was updated.
  • then I added simbuffers, but animations are still decided on simulation side, visual side just updates the entity.
  • for Vido I hacked a secondary animation channel to the simbuffer, but otherwise no change.
  • what we need is to do things properly like with actors:
    1. On sim side, only pos/ rot is calculated and written to simbuffer, with some hint what action is being done like idle, running etc...
    2. On visuals side, the logic from above post is applied.

TL;DR: Present rorbot is 100% hardcoded which prevents artists like Vido from really improving him. And while at it, it makes sense to make him 100% moddable.

@ohlidalp ohlidalp force-pushed the vido-rorbot-anims branch 2 times, most recently from 6e20061 to ec69f42 Compare September 24, 2022 10:33
@ohlidalp
Copy link
Member Author

This (very satisfying!) new debug UI shows the animation definitions and visualizes if, and why, they're running.
obrazek

Red flags block the animation.
Green flags let the animation run.
Yellow flags let the animation run, but some are missing or the animation is blocked.
Dark blue flags mean nothing blocks the animation.

@ohlidalp
Copy link
Member Author

Added (even more satisfying) inline display mode to 'Game anims' UI.
obrazek

@ohlidalp
Copy link
Member Author

I added support for bone blend masks, see https://ogrecave.github.io/ogre/api/1.11/class_ogre_1_1_animation_state.html#a06594a53335afbe8ccb67331e35a32fd

boneBlendMasksWorking400px

Example for the stock character.mesh (disables all upper body animation when running, see above GIF):

begin_bone_blend_mask
    anim_name "Run"
    bone_weight "hand.R" 0
    bone_weight "hand.L" 0
    bone_weight "forearm.R" 0
    bone_weight "forearm.L" 0
    bone_weight "upper_arm.R" 0
    bone_weight "upper_arm.L" 0
    bone_weight "shoulder.R" 0
    bone_weight "shoulder.L" 0
    bone_weight "head" 0
    bone_weight "neck" 0
    bone_weight "chest" 0
    bone_weight "spine" 0
end_bone_blend_mask

ohlidalp added a commit to ohlidalp/rigs-of-rods that referenced this pull request Oct 16, 2022
We have multiple formats with very similar syntax: rig def (truck), odef, tobj, character (see RigsOfRods#2942). This new parser supports all these formats and adds new features:
 - support for "quoted strings with spaces". I first implemented them in the new .character fileformat and I decided I want them everywhere, notably .tobj so that procedural roads can have custom names and descriptions for a GPS-like navigation system.
 - Editable memory representation. The document is a vector of Tokens (types: linebreak, comment, keyword, string, number, boolean.) which keeps the exact sequence as in the file. Tokens can be easily added/modified/removed without writing any extra custom code.
 - Serializability - saving the (possibly modified) file is as simple as looping through the Tokens array and writing them to a file. No custom code needs to be written for any file format.
 - Ease of binding to AngelScript: a single API can modify any fileformat. All the operations the user needs is 1. insert token 2. modify token 3. delete token.

Code changes:
 - TObjFileFormat.h: replaced char[] with std::string; renamed TObjFile to TObjDocument, renamed TObjParser to TObjReader.
 - TObjFileFormat.cpp: replaced scanf() with the new Reader API.
 - TerrainObjectManager.h: replaced char* with std::string in function args.
 - TerrainObjectManager.cpp: using the new Document API.
ohlidalp added a commit to ohlidalp/rigs-of-rods that referenced this pull request Oct 17, 2022
We have multiple formats with very similar syntax: rig def (truck), odef, tobj, character (see RigsOfRods#2942). This new parser supports all these formats and adds new features:
 - support for "quoted strings with spaces". I first implemented them in the new .character fileformat and I decided I want them everywhere, notably .tobj so that procedural roads can have custom names and descriptions for a GPS-like navigation system.
 - Editable memory representation. The document is a vector of Tokens (types: linebreak, comment, keyword, string, number, boolean.) which keeps the exact sequence as in the file. Tokens can be easily added/modified/removed without writing any extra custom code.
 - Serializability - saving the (possibly modified) file is as simple as looping through the Tokens array and writing them to a file. No custom code needs to be written for any file format.
 - Ease of binding to AngelScript: a single API can modify any fileformat. All the operations the user needs is 1. insert token 2. modify token 3. delete token.

Code changes:
 - TObjFileFormat.h: replaced char[] with std::string; renamed TObjFile to TObjDocument, renamed TObjParser to TObjReader.
 - TObjFileFormat.cpp: replaced scanf() with the new Reader API.
 - TerrainObjectManager.h: replaced char* with std::string in function args.
 - TerrainObjectManager.cpp: using the new Document API.
ohlidalp added a commit to ohlidalp/rigs-of-rods that referenced this pull request Oct 17, 2022
We have multiple formats with very similar syntax: rig def (truck), odef, tobj, character (see RigsOfRods#2942). This new parser supports all these formats and adds new features:
 - support for "quoted strings with spaces". I first implemented them in the new .character fileformat and I decided I want them everywhere, notably .tobj so that procedural roads can have custom names and descriptions for a GPS-like navigation system.
 - Editable memory representation. The document is a vector of Tokens (types: linebreak, comment, keyword, string, number, boolean.) which keeps the exact sequence as in the file. Tokens can be easily added/modified/removed without writing any extra custom code.
 - Serializability - saving the (possibly modified) file is as simple as looping through the Tokens array and writing them to a file. No custom code needs to be written for any file format.
 - Ease of binding to AngelScript: a single API can modify any fileformat. All the operations the user needs is 1. insert token 2. modify token 3. delete token.

Code changes:
 - TObjFileFormat.h: replaced char[] with std::string; renamed TObjFile to TObjDocument, renamed TObjParser to TObjReader.
 - TObjFileFormat.cpp: replaced scanf() with the new Reader API.
 - TerrainObjectManager.h: replaced char* with std::string in function args.
 - TerrainObjectManager.cpp: using the new Document API.
ohlidalp added a commit to ohlidalp/rigs-of-rods that referenced this pull request Oct 18, 2022
We have multiple formats with very similar syntax: rig def (truck), odef, tobj, character (see RigsOfRods#2942). This new parser supports all these formats and adds new features:
 - support for "quoted strings with spaces". I first implemented them in the new .character fileformat and I decided I want them everywhere, notably .tobj so that procedural roads can have custom names and descriptions for a GPS-like navigation system.
 - Editable memory representation. The document is a vector of Tokens (types: linebreak, comment, keyword, string, number, boolean.) which keeps the exact sequence as in the file. Tokens can be easily added/modified/removed without writing any extra custom code.
 - Serializability - saving the (possibly modified) file is as simple as looping through the Tokens array and writing them to a file. No custom code needs to be written for any file format.
 - Ease of binding to AngelScript: a single API can modify any fileformat. All the operations the user needs is 1. insert token 2. modify token 3. delete token.

Code changes:
 - TObjFileFormat.h: replaced char[] with std::string; renamed TObjFile to TObjDocument, renamed TObjParser to TObjReader.
 - TObjFileFormat.cpp: replaced scanf() with the new Reader API.
 - TerrainObjectManager.h: replaced char* with std::string in function args.
 - TerrainObjectManager.cpp: using the new Document API.
@ohlidalp
Copy link
Member Author

ohlidalp commented Oct 28, 2022

Purpose: to make the rorbot 100% moddable

Estimate: 2 man days. The hard part is done, the character-def fileformat is proven to work by Vido using dev-build.

Work to be done: ModCache needs to be updated to recognize rorbot mods. I've done this before, see ohlidalp@bf95ca9

@ohlidalp
Copy link
Member Author

ohlidalp commented Nov 2, 2022

I've added modcache support for character mods, and a RoR.cfg option to specify the default character, which you can also configure from Settings UI via selector. Soon I'll move the classic RORbot from resources directory to content directory.
image
image

@ohlidalp ohlidalp force-pushed the vido-rorbot-anims branch 2 times, most recently from dfa13be to b97e60d Compare November 3, 2022 12:56
@ohlidalp ohlidalp changed the title Extending character animation capabilities Make characters (player avatars) moddable Nov 6, 2022
@ohlidalp ohlidalp marked this pull request as ready for review November 13, 2022 22:50
@CuriousMike56
Copy link
Collaborator

Unfortunately I've been unable to load into any terrains with this.
Upon downloading and extracting the Windows build from GitHub, attempting to load a terrain gave me this:
image

01:09:52: [RoR|General|Error] Could not find character 'classic.character' in mod cache.

First thought was that maybe classic.character wasn't included by default, so I checked resources\skeleton.zip and strangely enough, realized that every file in that zip is duplicated:
image
This is also the case for all the other dev builds I've downloaded. They're definitely exact copies as attempting to extract them gives me an overwrite prompt. All other resource zips appear to be unaffected.

Continuing on, I went to move classic.character to My Games\Rigs of Rods\config which turns out that file was already created on launch. In RoR, the classic.character file appears in settings but the character selector is empty:
image
I searched mods.cache and didn't find any mention of the character file. There should definitely be an emergency fallback if no character files are found.

Then I tried your rorbotmod-tut1-rev1.zip file, placed that file into the mods folder and launched the game. The character selector now shows the mod:
image
Attempting to load any terrain now fails with this error in the log:

01:18:39:  ===== TERRAIN LOADING DONE 2af11UID-f1_testtrack.terrn2
01:18:39: [RoR|CVar]      sim_terrain_name:  "2af11UID-f1_testtrack.terrn2" (was: "")
01:18:39: [RoR|CVar]  sim_terrain_gui_name:  "f1_testtrack 0.8" (was: "")
01:18:39: Ogre::ItemIdentityException::ItemIdentityException: Cannot locate a resource group called '' for resource 'rorbotmod-tut1-rev1.character' in ResourceGroupManager::openResource at C:\Users\runneradmin\.conan\data\ogre3d\1.11.6.1\anotherfoxguy\stable\build\f7f69c899d0ea5d1d2b62305f3102aa1a22dede5\OgreMain\src\OgreResourceGroupManager.cpp (line 675)
01:18:39: [RoR|General|Error] Could not load character, message: Ogre::ItemIdentityException::ItemIdentityException: Cannot locate a resource group called '' for resource 'rorbotmod-tut1-rev1.character' in ResourceGroupManager::openResource at C:\Users\runneradmin\.conan\data\ogre3d\1.11.6.1\anotherfoxguy\stable\build\f7f69c899d0ea5d1d2b62305f3102aa1a22dede5\OgreMain\src\OgreResourceGroupManager.cpp (line 675)

I also tried copying classic.character to the mods folder, which makes it show up in the selector but still throws the above error.

Another (possibly unrelated) error I had when testing this build was when I tried to load another terrain after receiving the "failed to create character" error:
image
Disabling particle gfx doesn't solve this. I'm reporting this here as I can't reproduce it with other builds.

@ohlidalp
Copy link
Member Author

@CuriousMike56 Thanks for the exhaustive testing and sorry about the confusion with the default rorbot. The game now loads rorbots exclusively from the mods directory, so the config/classic.character won't be found anymore - I need to delete it. Also the classic rorbot needs to be uploaded to the content repo (alongside agora bus and DAF semitruck).

@CuriousMike56
Copy link
Collaborator

Adding the classic RoRBot zip to mods allows terrains to load now, but the character is giant and with no texture (same result as Tritonas on Discord). Loading your character mod crashes the game when loading the skeleton:

09:20:19: Creating resources for group bundle C:\Users\Mike\Documents\My Games\Rigs of Rods\mods\rorbotmod-tut1-rev1.zip
09:20:19: All done
09:20:19: Mesh: Loading rorbotmod-tut1.mesh.
09:20:19: Skeleton: Loading rorbotmod-tut1.skeleton

This brings me to the biggest problem I have with this PR, there's no fallback to the current hardcoded character. If that's not possible, then at the very least the default RoRBot absolutely should be kept in the resources directory and not treated as a mod. There's quite a few players out there who don't read the docs and assume mods are installed in the content folder. These are the same players that end up removing the other default content. If you find yourself on a populated multiplayer server try spawning one of the default vehicles, you'd be surprised to see that there's a good chance at least one person on that server doesn't have the vehicle installed.

With the current setup there's bound to be people who delete the character zip from the content directory thinking it's not required then come to #game-support wondering why they can't load any terrains.

@ohlidalp
Copy link
Member Author

ohlidalp commented Nov 15, 2022

@CuriousMike56 Thanks for testing, sorry about the giant size glitch (I forgot to add mesh_scale to the definition file). My idea is to migrate the default rorbot from "resources" directories to the "content" directory, as ZIP, alongside Agora and DAF Semi. If anyone messes with files in there it's their fight, I'd just add "reinstall the game" note in the "can't load terrain - no character" dialog and be done with that. My priority is keeping the codebase straightforward (=less bug prone), which means using rorbot mods exclusively.

EDIT: OK, I see that it's either me taking the hit of maintaining more code (the alternate codepath of loading rorbot from the resources), or you getting the hit of more idiots spamming #support. The problem is, the old rorbot code is gone - the game uses the .character definitions exclusively, and I did that to enable Vido to just extend the default rorbot without hassle. Hardcoding the defs would take that away.

@ohlidalp
Copy link
Member Author

ohlidalp commented Nov 15, 2022

@CuriousMike56 I think I figured out a setup which will satisfy everyone:

  • rorbot mods will live in /mods, be enlisted in modcache and selected using Selector UI in the Settings.
  • the default rorbot will also be a mod,enlisted in modcache and selected using Selector UI in the Settings. However, it's resources will be in /resources/character.zip. That way, the extra code for me to maintain will be minimal:
    • tell modcache to scan /resources/character* too.
    • If the chosen character mod isn't found, use this one.

@CuriousMike56
Copy link
Collaborator

CuriousMike56 commented Nov 15, 2022

Perfect, as long the default character lives in the resources directory I'm good with that 👍
So far the only odd thing I've noticed with the default character animations is that now the character T-poses when pressing the A/D keys and left/right arrow keys at the same time. This might be just due to Vido's updated animations not being completed yet (assuming they're a part of this PR?)

Current (latest master) behavior:

RoR_2022-11-15_11-12-38.mp4

Now:

RoR_2022-11-15_11-24-46.mp4

It appears that its not playing any animation in both clips, just that latest master is defaulting to the idle pose instead of T-posing.

@tritonas00
Copy link
Collaborator

tritonas00 commented Nov 15, 2022

Button now is too small, harder to catch. You could make SmallButton a Button again and properly align text with the button:

ImGui::AlignTextToFramePadding();
ImGui::Text();
ImGui::SameLine();
ImGui::Button();

Also pressing Cancel in the character selector makes main menu visible

What happens in MP with this? Like player A has character A, player B has character B and player A doesn't have character B installed for example. Will he be able to see the default character in this case? Or everyone see the character that they selected in settings?

This obsoletes the separate "upper/lower" body anim system.

* Character.cpp: no longer determines animations, only sets state & situation flags.
* SimBuffers.h, Network.h: now transferring flags instead of anim+time.
* GfxCharacter.cpp: determine animation from flags and other state info.
Top menubar => tools => Character pose util => tab "Game animations"

It also wonderfully explains how the new animation definition system works.
- Red flags block the animation.
- Green flags let the animation run.
- Yellow flags let the animation run, but some are missing or the animation is blocked.
- Dark blue flags mean nothing blocks the animation.
The default character is defined in file "resources/skeleton/config/classic.character" and loaded at startup.
New hotkeys:
* EV_CHARACTER_CUSTOM_ACTION_01 - 10 (F1 - F10)
* EV_CHARACTER_CUSTOM_MODE_01 - 10 (Ctrl+1 - Ctrl+0)
Switches skeleton to `Ogre::SkeletonAnimationBlendMode::ANIMBLEND_[AVERAGE/CUMULATIVE]`
Example for the stock character.mesh (disables all upper body animation when running):
```
begin_bone_blend_mask
    anim_name "Run"
    bone_weight "hand.R" 0
    bone_weight "hand.L" 0
    bone_weight "forearm.R" 0
    bone_weight "forearm.L" 0
    bone_weight "upper_arm.R" 0
    bone_weight "upper_arm.L" 0
    bone_weight "shoulder.R" 0
    bone_weight "shoulder.L" 0
    bone_weight "head" 0
    bone_weight "neck" 0
    bone_weight "chest" 0
    bone_weight "spine" 0
end_bone_blend_mask
```
The usage is equivalent to other mods: a ZIP/directory under 'ROR_HOMEDIR/mods' containing a '*.character' file.
To select character, open Settings UI, go to Gameplay tab, scroll to "Player character:" at the bottom and press "Select" button - the standard Selector UI will appear.
Character is always loaded with terrain. If the character fails to load, a "Cannot load terrain" messagebox appears and user remains in main menu.

The classic character resources were deleted from /resources and will be added to /content (a Git submodule).
You need a rorbot mod to use this branch. Without one, you won't be able to load a terrain.
Here is the classic rorbot remade as a mod (it will be uploaded to content repo later) and one test rorbot mod from a beginner tutorial:
RigsOfRods#294
Improved looks of the Settings/Gameplay/Select button.
Added fallback code to use default chracter if the configured custom mod isn't found.
All default character media moved under new directory /resources/classic_character - the deleted mesh/skeleton/material files were reintroduced, missing textures were migrated, a .character definition file was added.
Goal:
* to resolve ambiguity between "anim/animation" meaning either skeletal animation or game-defined animation. Ambiguity is bad.

Changes:�* game animations (defined in the .character file) ale now called Actions.  the `begin/end_animation` is now `begin/end_action`.
* ACTION_ flags were renamed to CONTROL_ flags.�
Recap of how the system works:
* A skeletal animation is a named animation track in OGRE '*.skeleton' file, like "Walk", "Swim", "Side_step"...
* User defines 'actions' via .character file, by specifying a skeletal anim name to play and game state flags which trigger/block it.
* SITUATION_ flags tell you what circumstances the character is in (on ground, in deep water, driving...)
* CONTROL_ flags tell you what controller input is the player giving (go forward, turn, sidestep...)
Fixed glitches in the classic character if player presses contradicting keys together.
More internal renaming to the new "anim=skeletal, action=game" terminology, see comments in previous commit.
Character net packets moved from Network.h to RoRnet.h, alongside the actor packet.
summary of changes:
- the built-in character was renamed from "classic" to "default"
- a new cvar 'mp_override_character' was added - configurable from MultiplayerSelector UI with buttons [select] [clear].
cvars: added sim_player_character_skin, mp_override_character_skin
character format: removed 'material_override', added 'character_guid' -- deal with it
rornet: added 'character_skinfile' (max 40 characters) to UserInfo
GameSettingsUI: added skin info next to sim_player_character info.
MultiplayerSelectorUI: added skin info next to mp_override_character info.
MainSelectorUI: all spawning and skin-choosing logic moved from `MainSelector::Apply()` to `GameContext::OnLoaderGuiApply()` - all in one place.
added guid to default.character in resources.
character now supports multiple materials (= submeshes + subentities, because each can use only one material)
The `m_current_selection` selection wasn't properly cleared.
Also LT_Character/LT_CharacterMP were detected wrong (`type` would be LT_Skin if skins were available).
I changed how player colors are applied - instead of relying on order of elements in .material file, there are names (aliases):

```
		 // The multiplayer colorization pass - must be named "ColorChange"
		pass ColorChange
		{
			scene_blend alpha_blend
			// The alpha mask texture unit - name doesn't matter.
			texture_unit
			{
				texture character-alpha.png
			}
			// The color texture unit - must be named "PlayerColor"
			texture_unit "PlayerColor"
			{
				colour_op_ex blend_current_alpha src_manual src_current 0 0 0
			}
		}
```
One .skin file can define multiple skins, so skins must be described by full name, not filename. The fix applies to both cvars (local configuration) and RoRnet.

Codechanges:
- CacheSystem.h: added search method NAME_FULL and helper func `MatchExact()`
- CacheSystem.cpp: more comments in `Query()`
- RORnet: renamed skin field + constant to clarify filename vs. name.
- Network.cpp - just reflect the renaming in rornet
- CharacterFactory: added helper `fetchCharacterSkin()` using the correct lookup for both local+remote characters.
- GameContext.cpp - `OnLoaderGuiApply()` - fixed configuration cvars to contain skin names instead of filenames
Problem 1: the arrays in GfxScene weren't updated correctly:�    std::vector<GfxActor*>            m_all_gfx_actors;
    std::vector<GfxActor*>            m_live_gfx_actors;
    std::vector<GfxCharacter*>        m_all_gfx_characters;�I was the person who introduced the arrays a long time ago (before message queue was estabilished) and they didn't exactly help, so now I dropped them again and I use the global arrays directly:
   App::GetGameContext()->GetActorManager()->GetActors()
   App::GetGameContext()->GetCharacterFactory()->getAllCharacters()

Problem2: The debug states array in GUI_CharacterPoseUtil was interfering between actors.
I fixed it by moving the array into GfxCharacter itself, and I updated the GUI to only manipulate player character.

Minor change: dropped the 'initialized' state tracking from GfxActor:�    bool                        m_initialized = false;�    bool                 IsActorInitialized() const  { return m_initialized; } //!< Temporary TODO: Remove once the spawn routine is fixed
    void                 InitializeActor() { m_initialized = true; } //!< Temporary TODO: Remove once the spawn routine is fixed�If there any visual glitches caused by this, they can be fixed later, especially since I noticed some existing singleplayer glitches to fix.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants