Checklist for porting to different 3d engine than OGRE.

only_a_ptr

Lead Developer
Administrator
Developer
Joined
Jan 31, 2018
Messages
133
Location
Prague, CZ
SPOILER: I'm not actually going to port Rigs of Rods to a different 3d engine because it would gain us nothing - our visual bottleneck are assets (materials, shaders, textures...), not the technology. The reason I'm writing this is to show that such port is technically possible, even doable, but not worth the effort - and why.

These concerns come up all the time: "OGRE is {limiting our visual fidelity/unpopular, so we get less devs/complicated/inferior to Unreal5/[insert whine here]}". The first thing to deal with here is terminology: OGRE is not "an engine", it's "a library"! ."An engine" in common player speak means "a game engine" (Unity/Unreal/ValveSource/SeriousEngine/Godot/...many others...). OGRE means "Object-Oriented Graphics Rendering Engine" so yes, it's in the name but don't get distracted, OGRE only provides 3d rendering and resource management, so it's a 3d rendering framework but not a game engine by any means. The difference is that game engines provide input/audio/networking/scripting/terrain system/possibly physics and collisions... and Rigs of Rods implements all of this itself. Technically, you could even consider Rigs of Rods a niche game engine. None of this is a showstopper for using a different 3d renderer, though, see following points:
  • App infrastructure/OS interfacing: we-re a free-standing C++ program with it's own `main()` function and we initialize everything manually. That means, we can drop or add whatever we want.
  • Filesystem access/ZIP archive handling: we use OGRE's resource management exclusively because it's safe with Unicode on Windows (fopen()/fstream are not!). This is OK because the resource system can be used separately from the rest of OGRE, in fact OGRE devs do that in their unit tests so there's an example.
  • String handling: we embed utf8cpp library for utf8 sanitization, and we use OGRE's string utils a lot. This is OK - we just have to keep OGRE around, no init needed.
  • Rendering window: we use OGRE to open one. We may either use the new renderer to open one (i'd prefer that), or open one ourselves using SDL2 or direct OS interaction.
  • Input: we're tightly coupled with OIS which was a go-to solution with OGRE for many years. We'll eventually have to port to SDL2 though for AppleOSX support. For the port I'd just keep OIS around. input.map etc.. would remain the same.
  • GUI: we use DearIMGUI which is renderer-agnostic. A big win for porting.
  • HUD: we use some OGRE overlays and some MyGUI (do not confuse with DearIMGUI!) layouts. None of that is portable, but a DearIMGUI replacement could be made in short time, with graphics, see https://github.com/RigsOfRods/rigs-of-rods/pull/2490
  • Networking: we use SocketW for raw TCP messaging. We can keep it or use anything else, really. RoRnet wouldn't have to change at all.
  • ModCache: would keep working like usual using OGRE resource system - as I already wrote, it's completely legit to use OGRE resource system separately from the rest of OGRE. Nothing would change for the user - just put .ZIPs and .SKINZIPs to 'mods' directory and you're good to go. In case the new rendering system insists on using it's own resource management or it's own formats, we'd have to code a "deployment" phase in the modcache regen process, which would copy (maybe convert and rename) the resources into a dedicated directory. Sensible and doable.
  • Terrn2/tobj/odef: our terrain fileformats, just text files, can be loaded using Ogre::ConfigFile like now.
  • Terrain/heightmap rendering: we use Ogre::Terrain subsystem to generate and render terrain from simple PNG/RAW heightmap. Every 3d library/engine can do that.
  • Terrain/heightmap collision: we use OGRE to query terrain height at certain position, for node collision. Actually, since that was a performance bottleneck (many nodes, DLL-call for each one) Ulteq made a hack (~2015) by porting the heightmap-query code from OGRE to RoR, but that created a bug - only single terrain page now works for collisions. I plan to fix this by restoring the direct-OGRE querying and adding a cache on top of it, because the resolution of the heightmap is never too big so caching just the few pixels around will do a lot. Anyway, I digress - for the port, we'd either keep the current Ulteq's solution (only getting the heightmap texture from the new renderer instead of OGRE) or use the new renderer directly - I'm sure it has the same function.
  • Terrain/static collisions: we have our own system for static collision meshes. It uses no dependencies, so OK to port. They're generated from OGRE meshes specified in ODEF files. For the port, nothing needs to be changed - OGRE lets you load and dismantle it's mesh format even without starting the renderer.
  • Meshes in general: a mesh is pretty much the same data in any renderer because it runs on the same hardware: just a vertex buffer (with format declaration) and index buffer. OGRE lets you load and dismantle it's mesh format even without setting up it's rendering, so this process is straighforward - just read from one fileformat and save to other. May be done on ModCache regen to save time.
  • Textures in general: probably no conversion needed. And even if, can be done on modcache regen, see ModChache point above.
  • Shaders: we use almost none except "nicemetal.cg". Since the .cg format (nVidia Cg toolkit) is a fossil, it probably won't be supported, but it won't really matter because any modern renderer supports PBS with metallic workflow, which I think is mostly equivalent (i never examined the shader, just assuming).
  • Terrain shading: we use Ogre::Terrain's custom shader generator, which nobody likes, even OGRE devs: https://github.com/OGRECave/ogre/issues/648 Anyway, we use only a basic alphablending setup with 3-4 textures and RGB blendmaps, it's controlled by OTC text file format which can be parsed without change, so I'm sure it would be trivial to read the file and set up the same texture blending in any different terrain system using any renderer. We'd probably get per-pixel lighting (lights would shine on ground again!) and PSSM3-shadows for free - same thing which OGRE RTSS grants and which OGRE devs are gearing towards: https://github.com/OGRECave/ogre/pull/1762
  • Materials in general: we use only the bare essentials,usually just diffuse map with the default per-vertex lighting (emulated FFP). sometimes a specular and normal maps are used. All 20 year old tech. For the port, just using a default shader template with our textures would probably be a big win, as any modern renderer would use per-pixel lighting and some PBS workflow by default. In OGRE, we could archieve the same results by using RTSS https://ogrecave.github.io/ogre/api/latest/rtss.html or asking Paroj about the state of PBR (Physically Based Rendering) https://www.ogre3d.org/2017/12/31/ogre-1-10-final-release and https://www.ogre3d.org/2020/06/12/ogre-ecosystem-roundup-5 - I found no mentions in the manual but it's mentioned in those news posts.
  • The truck fileformat: no change needed, we may benefit from new options in "managedmaterials".
  • Spawning trucks: no change for the physics, just setting up visuals would be a bit different, see above points.
  • Trucks/props: would behave as usual, see above point "Meshes in general".
  • Trucks/flexbodies: This is a pain point of current FPS performance: they're updated entirely on CPU and uploaded to GPU, wasting precious RAM->VRAM bandwidth. Normal mapping is disabled only because that would add another 3x4 bytes per vertex and put FPS to sleep. Porting this mess isn't worth it, too much hassle with OGRE's mesh interfaces in the update process. What is worth doing is remaking this system with vertex shaders (BeamNG did this years ago). It all works by loading a mesh, reading a "forset" node list from truck file and generating "locators" which pair a vertex with 3 nodes (ref, x, z) and offset (x,y,z). On each update, a reference carthesian axis system is generated from the 3 nodes and the mesh vertex is placed at position of ref node + offset. This is all done on CPU, then uploaded to GPU, each frame, ignoring the fact the vehicle may be outside of camera view. A sensible approach while staying with OGRE would be: generate the "locators"as usual, then mix them into the mesh itself (replace vertex XYZ position with the XYZ locator offset and put the nodes (3x2-byte unsigned int) into some auxiliary data channel like TEXCOORD1 (2x4-byte float) -> ends up with 2 bytes unused. Then, on each frame, pack the node positions to a constant buffer, upload it to GPU and do the flexing process in vertex shader. Normal mapping would be available because as I wrote earlier, the flexing process already supports it but it's disabled to save FPS. After this remake, porting would be trivial, just use different API for the mesh generation and constant buffer submission.
  • Trucks/flares: currently we use Ogre::Light (for the actual illumination) and Ogre::BillboardSet for the visual flare. Let's just use anything more modern a new renderer would offer.
  • Scripting: absolutely no change needed. Some functions which manipulate materials may be temporarily disabled or work differently, no big deal.
TL;DR: our visuals need a lot of improvement, yes, but OGRE offers much more than we currently use and it's open source, has great support and community. Porting to different renderer would get us nothing.

PS: I'm not claiming I could currently do all of the above - I have minimum experience writing shaders, I can't use RTSS yet and the flexbodies part is sheer theory, I never coded anything like that. Still, I'm not afraid of any such thing.
 

TankurfaceJr

Redneck Gearhead, PH.D
Joined
Apr 6, 2021
Messages
106
Location
Hall of Fame City, USA
I don't understand a lick of that, but I appreciate and respect all the people who do understand, as well as those who have made the game what it is. You rock.
 
OP
OP
only_a_ptr

only_a_ptr

Lead Developer
Administrator
Developer
Joined
Jan 31, 2018
Messages
133
Location
Prague, CZ
I changed my mind about this... I'm going to do an experiment where I stay with OGRE but make it theoretically replaceable. I'll split the application to 2 pieces:
  • Backend (RoR.dll) - will work exactly as now (RoR.cfg, input.map, RoR.log, mods... all loaded via OGRE) and it will draw GUI with DearIMGUI (to internal commandlists), except it will not load any meshes/materials/textures and will not render anything, it won't even initialize the OGRE renderer plugin.
  • Frontend (RoR.exe) - will load RoR.dll, initialize it, obtain SimBuffers from it (what are simbuffers?), load meshes/materials/textures (using OGRE), initialize OGRE renderer (ogre.cfg) and render the scene. It will also render DearIMGUI from it's internal commandlists (DearIMGUI si designed to work like this).
  • Frontend using different engine (?) - will work exactly like OGRE frontend except it will convert meshes/materials/textures to it's formats first.

It seems like a ton of work, but the fact is that the hardest part is already done: the current simulation code, in most cases, doesn't actively control the visual scene anymore, only writes it's state to SimBuffers (see above) and lets the graphics code process that. This project will basically just conclude it.
 
Top