Phazorknight / Cogito

Immersive Sim Template Project for GODOT 4
MIT License
873 stars 100 forks source link

Automatic shader tools for easy disabled clipping on viewmodels (ViewmodelSpace) #119

Closed FailSpy closed 6 months ago

FailSpy commented 6 months ago

I've been wanting to implement 2nafish117's viewmodel shader for a while now https://www.youtube.com/watch?v=NF-U5J92ivk

However I didn't love the fact that it required all materials to be reconfigured with a not-exactly 1-to-1 version of the StandardMaterial3D. Well, inspired by #116, where @Phazorknight shows a 4.0+ compatible version of the Viewmodel shader, I decided to see what it would take to dynamically create a "ShaderSpace" for viewmodel versions of wieldables. This is the result of that experiment.

Basically, I now have a Prefab 'ViewmodelBasePrefab.tscn' that if you make a parent of your mesh holder, it will go through all the materials available as its children (or children of children of children of children....) and configure it to be as close to the original StandardMaterial3D as possible.

This, I acknowledge, is most definitely not performant, however it only does it once per load of a wieldable's viewmodel. So as long as you're not trying to have hundreds of individual wieldables, this seems acceptable. I will probably see about making it into an editor tool later down the line so you can instead "bake" the materials and thusly ship it without the on-runtime generation

This all works via the ShaderSpace script I've written. If you choose to use that manually, you can set any shader you like, and if your property names share that of StandardMaterial3D names, it will automatically port a shader to that -- though right now the viewmodel concept is fairly hardcoded.

You can also change ShaderSpace's shader_override_enabled flag in code which will toggle the no-clipping feature for that specific wieldable on demand. Not sure if this actually has a use for anyone, but it's there!

Right now the ShaderMaterial conversion is very basic. It does not do Godot's dynamic conversion to ShaderMaterial so it doesn't include all the possibilities. I'm working on a better way to do this, but right now this works with all view model features used by Cogito's included wieldables.

EDIT: Turns out, there's no way to access Godot's internal Shader Conversion directly thanks to _get_shader_rid being entirely internal usage, BUT, the code that is used to actually convert shaders from StandardMaterial3D to ShaderMaterial is all readily available... in C++. Simple stuff: the code is legitimately just an appended string based on a whole bunch of conditionals. So convert that, and you have a GDScript implementation of it.

I'm sure it's still not perfect, but it's 95% of the way there. Again, not performant, but probably still fine for a couple of wieldables.

So I now have 'Material3DConversion' as well, which allows you to run Material3DConversion.create_shader_code(mat,variables,vertex) to inject variable code and vertex code into whatever ShaderMaterial would have been generated if you'd done it via the Editor. (Though this does not currently support anything but StandardMaterial3D -- this excludes ORM based materials)

FailSpy commented 6 months ago

Okay, I'm pretty happy with this experiment now. Opening the PR for review

Phazorknight commented 6 months ago

Just checking in if this is ready for review now? @FailSpy

FailSpy commented 6 months ago

Yep! @Phazorknight

FailSpy commented 6 months ago

Worth noting I'm very open to any requests/extensions on this, this is a large feature. Want to make sure it's solid.

Phazorknight commented 6 months ago

Overall the setup seems pretty straight foward and the results are great. I took the LaserRifle into the Lobby scene and had correct shadows from the big windows on the weapon model.

I think this will work for most use cases. Though I definitely noticed the performance spikes when first equipping wieldables. I'd definitely welcome the option to either A) convert the materials in the editor - or B) convert them during scene load, though I'd prefer option A as it would give more control to the developer while reducing load times.

Additionally, if you could provide a documentation paragraph that explains on how to set up a mesh inside a Wieldable to work with this, I'd say this would be ready to merge into main.

FailSpy commented 6 months ago

Though I definitely noticed the performance spikes when first equipping wieldable

Hm, equipping wieldables should not be causing a performance spike from this. It should be doing Option B pretty much as you stated: The materials are created when the scene loads the nodes, after that they should just have the new materials and the ShaderSpace code basically stays idle after.

FailSpy commented 6 months ago

@Phazorknight Give it a shot now. There's documentation directly in the scripts. If you want more, just ask!

You can now check (or un-check) 'Bake' on ShaderSpace and it will do the conversions in editor. Please note it stores the old version of the Material into a new folder .prebake so that way there's somewhere on disk that you can "un-bake" your material and get back the original.

I believe even without baking, the stutter that occurred on equip should be improved if not gone now as I've added seven ways to Sunday to stop any "rematerializing" code from running if it's not necessary.

Phazorknight commented 6 months ago

@FailSpy thanks so much for this!

So, after baking, Godot crashed twice for me, each time when switching to a different scene (once from switching from the Wieldable scene to the Player scene, and then after a restart, again when switching to a level scene). I've restarted it again, and everything was fine until I baked another wieldable, then the crash occured again on scene switching.

I was also not able to get a wieldable to not clip without baking the materials. Just checking/unchecking the Disable Viewmodel Clipping had no effect on my end on either the Pickaxe or the Toy Pistol.

A note on the stuttering: I think know this might've been caused by something else. I did get it again when I first equipped the laser rifle when loading this PR, but haven't gotten it since.

FailSpy commented 6 months ago

Yikes, any chance you managed to catch what crashed it? Haven't personally had my Godot editor crash in all my testing

FailSpy commented 6 months ago

Actually hmm, I may have a theory

Phazorknight commented 6 months ago

@FailSpy this is what the shell caught when it crashed, though it might not help you that much: image

FailSpy commented 6 months ago

Whaaaaaat. I wonder if that's your Godot editor trying to do an HTTPS connection for submitting the error??

FailSpy commented 6 months ago

https://github.com/godotengine/godot/issues/84966

Hmm, wonder if this has anything to do with it?

FailSpy commented 6 months ago

@Phazorknight aaactually, try this latest run. Should fix bakeless shaders, and I'm hoping it avoids whatever issue is causing your crash (which I suspect may be filesystem related)

FailSpy commented 6 months ago

Ready for review again. Curious to know if editor crashes still occur, as I've been completely unable to reproduce on my machine.

ksjfhor commented 6 months ago

So I tryed this PR out, and this is strange: When I first picked up the flashlight there was a little lag, same on the toypistol, but not much, only like a 0,3sec delay, but it was noticeable and never happened again after that. and I cannot place any wieldable for some reason, the ones that were already there work: https://github.com/Phazorknight/Cogito/assets/87974401/a62df8a3-d71b-48aa-b9d4-43443966bdac

You may need to pause to see them in the editor.

FailSpy commented 6 months ago

@ksjfhor Are you trying to place one in the world? If so, you need to place the pickup version of the asset e.g. Pickup_LaserRifle, not the wieldable laser_rifle.

Later down the line I might see about making a PR to unify this to make implementing wieldables easier, but for now that's how things are setup.

ksjfhor commented 6 months ago

@FailSpy not only do you implement this in a godlike speed, you also know what i did wrong. The light influence and the shadows on the models is great and the pickupable doesn't cast a shadow, I think that's good too, otherwise it looks weird because we don't have a player shadow. I get a little lagspike and delayed showing when I first equip a pickupable, but after that it seems that the shader is cached, not sure if godot or my amd driver. I can rerun the scene and the lag does not happen again.

This could be due to https://github.com/godotengine/godot/issues/61233 ?

https://github.com/Phazorknight/Cogito/assets/87974401/bd3c5340-84f9-4ae9-a516-f10090f71ca4

So maybe the shader could e.g. get loaded in the main menu behind the visible menu to avoid those lagspike ?

FailSpy commented 6 months ago

That issue you mentioned sounds like a perfect fit. Will investigate the potential solutions later.

Phazorknight commented 6 months ago

I get a little lagspike and delayed showing when I first equip a pickupable

I think thats the same spikes I experienced. Re-equipping in the same or in a different level scene didn't cause the same spikes, so it seems very much like a caching hiccup.

Ready for review again. Curious to know if editor crashes still occur, as I've been completely unable to reproduce on my machine.

Gave it a try just now and had no crashes. Though it seems like the ability to "bake" in the editor has been removed, so that might've helped. Results seem great so far! Let me know if you have any more changes planned or if we should try merging this in.

FailSpy commented 6 months ago

@Phazorknight Hm, bake should not be removed! Is the checkbox not there?

What I changed about baking is that it no longer attempts to save the resource files to disk, and instead expects the dev to save any original materials that they might want to convert back. Did this because I suspected that automated filesystem access may be causing issues, so instead it gets stored into the ConvertedMaterial's memory. My concern is I'm not sure how Godot handles unaccessed attributes like that, so it very well might increase RAM usage, but shouldn't do it by much as the StandardMaterial3D resource itself is fairly small.

I'm currently investigating ways to do shader precompilation to stop the lagspike on equip. Once the shader is in the cache, it should be fine.

FailSpy commented 6 months ago

Out of curiosity to @ksjfhor and @Phazorknight: equipping a wieldable, lagspike, then equipping a different wieldable... lagspike or no lagspike?

I would test it myself but I don't seem to get the same shader compilation lagspike.

ksjfhor commented 6 months ago

No lagspike on other wieldable, just the first. A 4.x shader pre render is mentioned as the last comment in the godot issue from earlier. May this lag be an amd thing ?

Phazorknight commented 6 months ago

Hm, bake should not be removed! Is the checkbox not there?

Nevermind, I think I haven't properly pulled the PR on my end. It's there. Everything seems to work great!

Lag spike

The spike on my end is only the first time I equip a wieldable, and it doesn't seem consistent, but I haven't tested this extensively. I'm not really considering this a major issue at the moment.

I might see about making a PR to unify this to make implementing wieldables easier

Please let me know what kind of changes you're considering (maybe in a Discussion thread).

Otherwise I feel like this PR is about ready to merge into main, unless there are more objections.

FailSpy commented 6 months ago

I think I successfully implemented shader precompilation. Fairly hacky, but from looking at the various threads about shader precompilation, the way I'm doing it may actually be the cleanest way without recompiling the whole of the Godot engine, and Vulkan... When precompilation is enabled, you'll probably face some stutters at the start of the scene, but hopefully this means the rest of it is smooth.

If this works on @ksjfhor or @Phazorknight's system to get rid of the lagspike-on-equip, I'm happy to have this be merged!

EDIT: Two minor changes below to fix two minor derps, actually happy good now! :P

FailSpy commented 6 months ago

A 4.x shader pre render is mentioned as the last comment in the godot issue from earlier.

Oh bugger, I completely missed that. I'm quite happy with my version though, it's fairly similar, but it doesn't require any nodes to be added to the scene manually. If you choose to precompile a shader in ShaderSpace/ViewmodelSpace, it will do the magic itself, and reliably keep it off screen.

As well, other scripts can hook into it the same way. I also have a queue system implemented in mine to make it so the game only tries to compile one shader per frame, allowing the game to not go too long between frames and thusly the game can keep up.

FailSpy commented 6 months ago

Big ol' commit fixup/squash done. So long as its given the okay by you @Phazorknight, I'm very happy with this PR.

ksjfhor commented 6 months ago

So with the newest fetched up, godot starts the project with:

scene/main/viewport.cpp:137 - Viewport Texture must be set to use it. scene/main/viewport.cpp:127 - Viewport Texture must be set to use it. scene/resources/resource_format_text.cpp:284 - res://COGITO/Assets/Shader/ShaderLoader.tscn:21 - Parse Error: Failed loading resource: res://COGITO/Assets/Shader/ShaderLoader.tscn. Make sure resources have been imported by opening the project in the editor at least once.

So I wanted to open the ShaderLoader.tscn and: grafik

But as you can see in the screenshot, when I run the project the messages are gone, and also the lagspike, but I am not sure if this is due to some caching on my end .... "It just works" - Todd ?

FailSpy commented 6 months ago

It just works.

Anyways, that's weird that it got invalidated/corrupted. I've modified the script now because I realized there's a way to do it without a scene, and I think the shader precompiler is even more reliable now after remembering custom_aabb

Give it a shot now.

FailSpy commented 6 months ago

Fun tidbit, you can apply ViewmodelSpace to anything, and you get wallhacks! image

ksjfhor commented 6 months ago

It just works sing Without a doubt, no more errors or lagspikes or anything. Well done !