RigsOfRods / rigs-of-rods

Main development repository for Rigs of Rods soft-body physics simulator
https://www.rigsofrods.org
GNU General Public License v3.0
991 stars 175 forks source link

:angel:Script: OGRE bindings for editing scene, meshes and textures - with examples #3030

Closed ohlidalp closed 3 months ago

ohlidalp commented 1 year ago

EDIT: This branch grew much beyond the original scope; see The End Goal See also updates at the end of this post.

Also remember:

//     #=====================================================================
//     #              #### WARNING - DANGEROUS API ####
//     #  OGRE objects don't use reference counting
//     #    - __ N E V E R __ keep pointers across `frameStep()` invocations!
//     #  Prefer always looking up the resource from OGRE - slower but safer.
//     #=====================================================================

~~Following text is the original opening post:~~

Work smarter, not harder.

This started as a Discord debate about shift stick animation and how to make it smooth. I half-jokingly suggested to use skeletal animation in the prop which would be controlled from scripting, and to my surprise that idea was supported. So, the primary goal of this PR is to bind OGRE's skeletal animation controls to AngelScript, for smoother shifting :)

The strong point of OGRE renderer is that the internal API is very well organized and documented. To display a 3D model in scene, you load Ogre::Mesh, use Ogre::SceneManager to create Ogre::Entity of the mesh and attach it to Ogre::SceneNode. To do the skeletal anim, you get Ogre::AnimationState from the entity and that's it - you call setTimePosition() or addTime() to play it. It's all in the docs: https://ogrecave.github.io/ogre/api/1.11/. Basically this is something modders could easily do directly, if it just wasn't C++. Since I already need to bind Ogre::Entity and so on for the primary goal, why not do it from the ground up... so the secondary goal here is to let modders load an arbitrary mesh, position/rotate/scale it in scene ... and play skeletal anims.

Finally, I love diagnostic tools, and OGRE also allows user to enumerate all it's objects and traverse hierarchies. Since I already had to bind Ogre::SceneManager for the secondary goal, the only extra step for complete OGRE integration is to bind Ogre::Root global object. I did so, with a syntax that mimics the C++ side almost indistinguishably. This is a screenshot of the ogre demo script which shows it in action: obrazek Before you ask... yes, our scene hierarchy is a complete mess. We have 2 scene managers (technically legit but what's the other for?), the root scene node has 148 direct children and there's an unnamed node with 341 children (WTF? you need 1 node for each prop/flexbody/character/static objects ... but I'm on an empty map with 1 Mini!). Of course, the important point here is that now this internal mess which isn't normally seen (in C++ you use pointers, that's why names are mostly empty) had been brought to daylight.

UPDATE (Corrected 2023-04-01): Secondary goal is met. You can now load/position/rotate/animate an arbitrary mesh from just AngelScript. The bundled script 'ogre_demo.as' now has 2 sections: Inspector (the above screenshot) and Pose Demo (the below screenshot). To run the script, open in-game console and say loadscript ogre_demo.as.

obrazek

UPDATE 2 (2023-04-01): I just realized I'm not done here yet. The primary goal explicitly refers to stick shift animation, and even though the animation bit is in place, a data source for the shift status is missing. I'm going to need to add bindings for it, and that means binding the RoR::EngineSim object, which encapsulates engine+gearbox+differential and it's going to be a piece of work by itself. I'm going to create a separate branch for it so I can properly describe and present it, complete with demo script.

UPDATE 3 (2023-05-28): I decided to separate out the shiftstick/engine part to #3048 and get this merged because I already have other ideas of building on top of this, and it's large enough already.

UPDATE 4 (2023-09-18): I ended up dealing with the shift stick the simple way: #3065 and in the meantime I also developed more OGRE-AngelScript bindings in the script_editor branch: https://github.com/RigsOfRods/rigs-of-rods/pull/3074#issuecomment-1693383584 and just now I moved them here.

UPDATE 5 (2024-02-11) I've finally dealt with the Tuning branch and I'm back on this. I've set an end goal of this branch to optimize performance of our terrains, especially "Community map", and in the process add the ability to generate meshes and textures. See https://github.com/RigsOfRods/rigs-of-rods/pull/3030#issuecomment-1937363587

UPDATE 6 (2024-02-26) All the necessary script extensions are in place, I'm working on the tool

UPDATE 7 (2024-03-14) I decided to merge this PR "early" (while the advertised TerrnBatcher tool is still in alpha stage) because the bulk of this PR is really the script extensionsw would that enable this tool to happen - but those have a vastly greater potential and opportunities to use it have been springing up all over the place lately.

ohlidalp commented 1 year ago

Progress. I added Ogre::MovableObject to the API and inspector. in OGRE, everything you can render is a movable object - most often 'Entity' but also 'ManualObject', 'ParticleSystem', 'BillboardSet' etc. It also controls the visibility and shadows, so I added checkboxes for it.

Finally, I cleaned the scene graph a lot, at least until you load any mods :) I named the camera/character nodes and grouped particle nodes by type (those took up most of the space): obrazek

willmichals commented 8 months ago

Tested & works fine!

ohlidalp commented 4 months ago

End goal: improve terrain performance

Our terrains suffer of low FPS due to overly high batch count (= number of draw calls). This is because we use many low-poly meshes and materials. The graphics chips and drivers like large meshes with a single material containing a big texture atlas. Number of polygons or texture size doesn't matter - one mesh with single material counting 1M polygons will render faster than multiple meshes (different material each) counting 1K polygons together. The way to go is to simply ditch our existing meshes and generate new ones from their geometry and textures, merging them together for optimal rendering.

There are ~3~ (EDIT: 4) key ingredients to this:

I've finally dealt with the Tuning branch and I'm back on this. The tuning branch is now merged and brings project support - the ability to create subdirs under Documents/My Games/Rigs of Rods/projects and populate them with files from an existing mod. The intended use was vehicles, but it will work with terrains just the same.

New feature: arbitrary mesh generation

The screenshot below showcases a new example script 'example_ogre_MeshedConcrete.as' (run it using loadscript command in console) which shows how to generate a mesh which follows the shape of the terrain, effectivelly forming a meshed decal. The material used in the example is "taxiwayconcrete" but you can use any material. obrazek

ohlidalp commented 4 months ago

Texture blitting works!

I've just successfully added the ability to edit textures via AngelScript. This literally modifies the pixels in the texture by specifying source image and destination box. OGRE can do more but this is a start.

The screenshot showcases a new example script 'example_ogre_textureBlitting.as' - you can try it out by opening console (Hotkey ~ or topMenubar->tools->ShowConsole) and using loadscript command. NOTE: the example script only blits to topmost mipmap, so to see the effect, you must be close to the object! This will be improved. obrazek

Example code:

    // src image
    Ogre::Image gSrcImg = game.loadImageResource("sign-roadnarrows.dds", "TexturesRG");
    // dst texture
    Ogre::TexturePtr gDstTex = Ogre::TextureManager::getSingleton().load("character.dds", "TexturesRG");
    // let the MAGIC happen
    Ogre::HardwarePixelBufferPtr pixbuf = gDstTex.getBuffer(/*cubemap face index:*/0, /*mipmap:*/0);
    Ogre::PixelBox srcPixbox = gSrcImg.getPixelBox(0,0); // getPixelBox(cubemapFaceIndex, mipmapIndex);
    box gDstBox; // where you want to put it.
    pixbuf.blitFromMemory(srcPixbox, gDstBox);
paroj commented 4 months ago

note that Ogre already provides an API to do this, given you can use the same material for all of the entities: https://ogrecave.github.io/ogre/api/latest/tut__static_geom.html

ohlidalp commented 4 months ago

I'm aware, except that's a no go given the state of our community. We have loads of separate meshes, many using multiple submeshes with separate materials, and a long history of doing stuff exclusively this way.

Writing a custom batching tool is really the only viable way:

paroj commented 4 months ago

Writing a custom batching tool is really the only viable way:

my point was that you can use StaticGeometry inside your tool to take care of merging the meshes. It already handles SubMeshes and LOD. Your tool would then just have to merge the materials beforehand.

ohlidalp commented 4 months ago

Point taken, thanks, but I'm determined to try by hand first, to explore and demonstrate the possibilities of the script bindings. Our terrain system already relies on generating meshes (procedural roads) and I intend to improve and extend these.

PS: also LODs are mostly non present on meshes produced by our community:D

paroj commented 4 months ago

you might want to consider LOD in the design of this and auto generate them at some point though: https://youtu.be/hf27qsQPRLQ?si=W4x8w8QCDqJrUdM-&t=705

https://ogrecave.github.io/ogre/api/latest/meshlod-generator.html

ohlidalp commented 4 months ago

Vertex data reading works

I opted to helper functions here because the raw vertex/index buffer API is involved and most importantly relies on retyping void* pointers which isn't a thing in AngelScript.

New script API:

See example script "example_ogre_vertexData.as" (on screenshot) - notice the texture contains a complete face but only one half of it is covered in texcoords. This isn't a bug in the tool but a quirk of the mesh - it actually uses that one half of texture to cover both halves of the face. obrazek

ohlidalp commented 4 months ago

Added OGRE Mesh/Material bindings + example

This introduces 'example_ogre_terrnBatcher.as' which shows how Mesh/Material API works and will evolve into a production tool.

THE INTENDED USE of the future completed TERRN BATCHER:

  1. You start with a list of OGRE SceneNodes - see green box.
  2. you pick those you want to batch together - see orange box.
  3. [WIP] The tool generates one mesh which contains all those picked (keeping position, scale and rotation) and cover them in single material containing all the textures.
  4. [TBD] The tool remembers what batches were done as a 'schedule', and allows saving/loading the schedule as file. Modders can share and improve the schedules.
  5. [TBD] Modders can launch the tool directly from .terrn2 and auto-execute a schedule on map load.
  6. Profit

obrazek

ohlidalp commented 3 months ago

I've just reverted the title of this PR from the intermediate "TerrnBatcher - terrain optimzation tool" to the original "AngelScript bindings for OGRE scene and stuff" because that's really what the bulk of the code here does, and I want to merge it already because lately I've been mentioning it almost in every my response in the sense "The PR 3030 would let you do this...", so let's not hold all those ideas back any longer. I'll create a new, dedicated branch for the TerrnBatcher script.