Skip to content

Adjustable LODCutoffs#115

Open
Strogoo wants to merge 6 commits intoFAForever:masterfrom
Strogoo:LODs
Open

Adjustable LODCutoffs#115
Strogoo wants to merge 6 commits intoFAForever:masterfrom
Strogoo:LODs

Conversation

@Strogoo
Copy link
Copy Markdown

@Strogoo Strogoo commented Apr 28, 2025

Added 2 functions to UI:

SetGroupLODMult(int groupID, float multiplier)

ApplyMultToWorstLODOnly(true/false)

MeshGroupID should be added to Mesh blueprint (directly or via blueprints.lua). ID should be from 1 to 127. Then using SetGroupLODMult you can change LODCutoffs of given group on the fly.

изображение

ApplyMultToWorstLODOnly() is set to False by default. This means multiplier will be applied to all avaliable LODs (LOD0, LOD1 etc). When True - only worst LOD is affected. For example:

                   Default LODs:     LOD0 = 50, LOD1 = 150.
Mult = 2, ApplytoWorst = true:     LOD0 = 50, LOD1 = 300
Mult = 2, ApplytoWorst = false:    LOD0 = 100, LOD1 = 300

One more UI function:

SetSmallShadowCutoff(float distance)

Requires additional filed in mesh bp IsSmallObject = true.

изображение

SetSmallShadowCutoff(distance) default is 0 (means this check is disabled). Setting it to any number above 0 will disable shadows for small objects (IsSmallObject = true in mesh bp) at certain zoom level. Note that distance is not a camera zoom value, but a distance between current camera pos and an object. So it works similar to LODs, when objects at corners dissapear a bit earlier.

shadows

@4z0t
Copy link
Copy Markdown
Member

4z0t commented Apr 28, 2025

Don't post links to executable here please. Use Zulip for that.

@Garanas
Copy link
Copy Markdown
Member

Garanas commented Apr 28, 2025

Please only share executables through Zulip.

The example that you share here is a mesh blueprint. Does this apply to all mesh blueprints? For example, would it apply to props? Does it apply to other LOD values, such as those of emitters?

edit: Ha, @4z0t beat me to it 😄 !

@4z0t
Copy link
Copy Markdown
Member

4z0t commented Apr 28, 2025

First glance at code: too much asm to process and check. As I said before I doubt I will approve it.

@Strogoo
Copy link
Copy Markdown
Author

Strogoo commented Apr 28, 2025

Yea sorry for too much asm and exe posting :). I think Moho::Mesh::ComputeLOD can be rewritten in C++ completely. The function itself is pretty simple and then a lot of asm code will gone. I'll think about it.

@Garanas Yea, it applies to any meshes (props too). Sadly, doesn't work with emitters and other things

@Strogoo
Copy link
Copy Markdown
Author

Strogoo commented Apr 30, 2025

@Garanas Funny fact (it's probably well known but still) - shadows is what affects performance A LOT when there are a lot of small objects on the screen. I imagine how many draw calls they need...
sh

You can disable shadows at certain zoom automatically, but the thing is that I want to see CZAR's shadow (on screenshot) and don't want small shadows when zoomed out. This is probably one more thing that needs patching :) Adding ability to disable small shadows at certain zoom

@Strogoo
Copy link
Copy Markdown
Author

Strogoo commented May 2, 2025

Added

SetSmallShadowCutoff(float distance)

I'll add description to first post

@Garanas
Copy link
Copy Markdown
Member

Garanas commented May 3, 2025

@Garanas Funny fact (it's probably well known but still) - shadows is what affects performance A LOT when there are a lot of small objects on the screen. I imagine how many draw calls they need... sh

You can disable shadows at certain zoom automatically, but the thing is that I want to see CZAR's shadow (on screenshot) and don't want small shadows when zoomed out. This is probably one more thing that needs patching :) Adding ability to disable small shadows at certain zoom

This is a great observation!

@Garanas
Copy link
Copy Markdown
Member

Garanas commented May 4, 2025

@Strogoo I think this pull request can be quite meaningful. But it's important that the focus is on making it more maintainable. This also includes the comment of @4z0t at #115 (comment).

I'll come with some name suggestions and other (relatively minor) adjustments later today or tomorrow. I'm trying to see what type of control we to have in Lua over these values.

@Strogoo
Copy link
Copy Markdown
Author

Strogoo commented May 4, 2025

@Garanas Yep, as I said before I like to make raw patches in pure asm just to make sure it actually works and can be useful. Next step is polishing the code (if needed), probably in cooperation with @4z0t , since he has much more C++ experience. Feel free to suggest different namings and other things that should be changed.

@Garanas
Copy link
Copy Markdown
Member

Garanas commented May 5, 2025

Here's my thoughts about these changes:

Overall: positive! Especially the ability to adjust the shadow cutoff is going to be useful. I think it may be even more meaningful then the mesh group IDs because of your observation in #115 (comment). Which was not well known 😄 !

About the naming convention:

  • MeshGroupId -> LODGroup, defaults to 0 (remains a number).

  • SetGroupLODMult -> SetLODGroupMultiplier

  • ApplyMultToWorstLODOnly -> Remove it

  • IsSmallObject -> ShadowLODMultiplier, defaults to 1.0 (changes from a boolean to a number).

  • SetSmallShadowCutoff -> remove it

Reasoning: these values are not about vertices, triangles, textures or anything related to the mesh. They're related to the LOD values that determine when the mesh is rendered. And now that's clear. By adjusting the type of IsSmallObject to a number we no longer need the console command SetSmallShadowCutoff. Because now we can just tweak it in the blueprint on an individual basis.

The use of ShadowLODMultiplier will be great for trees and tree groups!

I don't see the use of ApplyMultToWorstLODOnly. The issue is not in the number of vertices. The issue is that we can't feed the GPU fast enough, especially when we talk about shadows. It only complects the solution by introducing another variable. And there's no clear purpose for it. I think it should be removed.

On the Lua side we can introduce game options as sliders to make calls to SetLODGroupMultiplier. Can you share the latest executable on Zulip? Then I can make a draft pull request.

@Strogoo
Copy link
Copy Markdown
Author

Strogoo commented May 5, 2025

изображение
ApplyToWorst is very important imo. Not because of performance gain (there is no), but from visual perspective. Let's say I want to set tree's LODs to x2-x3 on Seton's for first 10 minutes (with UI mod). Without ApplyToWorst they look really bad (see screenshot). I think in most of the cases you don't really want to aply mult to both LODs, only to worst, to be able to see things at higher zoom level (where "worst" LODs looks better). Anyway having this function cost nothing so let it be I think.

As for shadow cutoffs, would be nice to have full control with UI function, instead of BP value (or mb change to float but add funtion SetShadowLODGroupMultiplier ? Because again, in edge cases (x2-x3) default bp values (like even 0.5) will not be enough I think.

123

ADD: Screenshot was made with shadows ON, without shadows it looks even worse.

изображение

@Strogoo
Copy link
Copy Markdown
Author

Strogoo commented May 5, 2025

Also about LODGroup, defaults to 0 (remains a number). You can place LODGroup = 0 in bp as placeholder, but it will be ignored (can't use SetLODGroupMultiplier(0, mult)). That's because I use 0 as null in asm (like cmp grouID, 0x0 then if exists we proceed. Don't want to touch this code tbh :). So anything that needs multipliers should start with 1

@4z0t
Copy link
Copy Markdown
Member

4z0t commented May 5, 2025

Мне не нравятся, как сделаны некоторые вещи в принципе. В частности то, что ASM перемешан с C/C++. У меня пока особо времени (и желания) смотреть нет в IDA, так что если у тебя 100% уверенность, что все сделано верно и @Garanas видит необходимость в данном функционале, то я не против. Может быть у @Hdt80bro глянет, что да как. Могу сказать, что лишь в конце месяца я буду готов глянуть, ибо у меня дела IRL.

@Strogoo
Copy link
Copy Markdown
Author

Strogoo commented May 5, 2025

Там возможно еще часть функционала уберется по итогу (смотри тему в дискорде), так что asm будет поменьше. В целом, я процентов на 90 уверен в коде. Надо просто прогнать еще в реальной игре, что б 100% крашей не было.

@Strogoo
Copy link
Copy Markdown
Author

Strogoo commented May 5, 2025

LODGroup = int from 1 to 127 in MeshBp

SetLODGroupMultiplier(int group, float mult)
ApplyMultToWorstLODOnly(bool)

SetShadowCutoffGroupMultiplier(int group, float mult)  -- if mult <=0  or mult >1 then mult = 1.


void ProcessBpFieldValues()
{
if (not strcmp(fieldName, meshGroupField)) //(not strcmp) == equal
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not is something that isn't common for C/C++, use ! or == 0.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, I just hate !. It's really harder to notice compare to not. Will change to == 0 I think?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For comparisons, == 0 is the most common because the return value isn't supposed to interpreted as a boolean - it's a three-way comparison, yielding a number less than, equal to, or greater than zero. So people use < 0, == 0, and > 0.

[ebpStorage] "m" (ebpStorage),
[edxStorage] "m" (edxStorage)
:
);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer you change the MeshBlueprint's RType in the builder function at 0x005187F0 than adding special cases for each new RField.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, yea I think you are right. My thought was that it would be nice to have such "dirty" hook where we check literally all bp fields at one place and then process them as we want (not necessarily adding them to RBlueprint, but mb something else). Yes it takes more time because of string compare etc., but we are at game loading screen here, so doesn't really matter.

But yes, for this exact case it's ptobably better to just change bp's RType.

Copy link
Copy Markdown
Author

@Strogoo Strogoo May 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

изображение
actually I forgot about this, so let's leave this hook as is :) It's really easier to go this way in case with LODs (and will be easier to use in future for other patches I think).

Comment on lines +5 to +8
SECTION(1, 0x004CF7F2)
"jmp "QU(GetBpFieldValues)";"

SECTION(2, 0x004CF7F7)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the two sections are that close, for the amount of reading each new section takes up, I'd rather only have one.

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.

4 participants