hexabits / nifskope

Other
245 stars 54 forks source link

ViewState confusion #83

Closed johnsone006 closed 2 weeks ago

johnsone006 commented 2 months ago

So, from what I understand about the setOrientation method of the glview.cpp file, the default ViewState of the file should be Top View, because that is the only view that doesn't get rotated. However, the Top checkbox is never the one that's selected when Nifskope opens for me. It's the Front checkbox that's selected. On the other hand, I don't think it's always the actual front view of the .nif that's showing because I have come across several meshes that don't show up at all until you change the view. I'd appreciate some clarification, please.

gavrant commented 2 months ago

Hey! The default view on (re)loading a mesh is Front, see ogl->setOrientation( GLView::ViewFront )in NifSkope::onLoadComplete (nifskope_ui.cpp). I'd say, Front is better than Top as the default for many, if not most, meshes, but yes, it occasionally backfires for some "flat" ones. Accidently, a few days ago I tried the Blender-style default view in NifSkope - the middle between Front, Top and Right, but decided to stay with Front (this could be a matter of habit on my part though).

Top View ... is the only view that doesn't get rotated

Could you clarify the "doesn't get rotated" part? I just tried switching to Top View, then rotating the camera with the mouse or by clicking on Flip View in the toolbar, it did get rotated.

I have come across several meshes that don't show up at all until you change the view.

Could you upload an example of those meshes?

johnsone006 commented 2 months ago

If you look at the glview.cpp file (nifskope/src/glview.cpp), you will see a method called void GLView::setOrientation( GLView::ViewState state, bool recenter ). If you take a look at the code block for that particular method, you will see that the only view that is made by calling SetRotation with parameters of all zeros is the TopView.

And actually, I can't find a great example of what I was talking about-prior to this, I thought that pretty much all the wall meshes that are one-sided don't show up at all, but now the ones that I have found simply show the back of the mesh. That said, the fact remains that I do not think that the front view is the same as the view Bethesda intended to be the front, and I'll explain why. You see, when 3D artists are creating meshes, in order to save time and money they won't bother creating the parts of the mesh that aren't intended to be seen by the camera. I think that's why many of the floor, wall, and ceiling meshes appear to only have one side to them. Thus, the fact that this mesh is a wall that is 1) not intended to look invisible to the player and 2) nevertheless showing up mostly invisible, to me, is a pretty strong indication that the front view Bethesda intended and the front view nifskope creates are different views. image_2024-07-07_134650474

gavrant commented 2 months ago

The 3 number arguments of GLView::setRotation are the Euler angles that "describe the orientation of a rigid body [the viewport camera in our case] with respect to a fixed coordinate system". In the "classic" Euler angles, (0, 0, 0) happens to match the Top view, but there are alternative forms, with (0, 0, 0) matching Front, Right or whatnot. So I think setRotation(0, 0, 0) here does not represent the intended default for the camera, it's just a manifestation of the math done further down the "pipeline", in GLView::paintGL. Or in other words, I think (0 ,0, 0) matching the Top view in NisSkope is a sort of coincidence, without any deeper meaning.

As for "what's the real front view of a model?", generally there is no such thing as the "intended front" for meshes. Yes, for certain kinds of meshes you must follow the unwritten rules of correct orientation for them to work properly in the game (for example, the stuff worn by actors: skins, hairs, armors, etc.). But for level statics it usually does not matter if for whatever reason on export the modeler rotates a model by 90 or 180 degrees in the horizontal plane, because most of the times level designers have to adjust its rotation to the layout of their level anyway.

BTW, in this respect NifSkope behaves exactly the same as the model preview form in Bethesda's own Creation Kit, See the attached screenshot below with the same WHIntWoodWallWood01.nif as on your screenshot. So to actually see that wall piece in CK, you must rotate the camera there too. whintwoodwallwood01

johnsone006 commented 1 month ago

The 3 number arguments of GLView::setRotation are the Euler angles that "describe the orientation of a rigid body [the viewport camera in our case] with respect to a fixed coordinate system". In the "classic" Euler angles, (0, 0, 0) happens to match the Top view, but there are alternative forms, with (0, 0, 0) matching Front, Right or whatnot. So I think setRotation(0, 0, 0) here does not represent the intended default for the camera, it's just a manifestation of the math done further down the "pipeline", in GLView::paintGL. Or in other words, I think (0 ,0, 0) matching the Top view in NisSkope is a sort of coincidence, without any deeper meaning.

As for "what's the real front view of a model?", generally there is no such thing as the "intended front" for meshes. Yes, for certain kinds of meshes you must follow the unwritten rules of correct orientation for them to work properly in the game (for example, the stuff worn by actors: skins, hairs, armors, etc.). But for level statics it usually does not matter if for whatever reason on export the modeler rotates a model by 90 or 180 degrees in the horizontal plane, because most of the times level designers have to adjust its rotation to the layout of their level anyway.

BTW, in this respect NifSkope behaves exactly the same as the model preview form in Bethesda's own Creation Kit, See the attached screenshot below with the same WHIntWoodWallWood01.nif as on your screenshot. So to actually see that wall piece in CK, you must rotate the camera there too. whintwoodwallwood01

Okay, let me clarify. By the "intended front view" of a mesh, I mean the side of the object the player will see in game when approaching the object head on. In the case of walls, it is the part of the mesh that is visible at all.

This matters because I'm developing a program that automatically opens and closes user-specified .nif files in NifSkope for the purpose of screenshotting the nif files, and then saving the screenshots in a user-specified location. This program also allows the user to programically change the view of the .nif. Primarily because as we have already established, NifSkope does not always load .nif files in such a way that would yield useful screenshots with the view as-is. It would be a lot simpler if NifSkope would make the Front view be what I think of as the front view. But that's just a suggestion, it's really not a big deal because the user can always just redo screenshots at different views. Thanks for the chat, by the way. I really appreciate this.

johnsone006 commented 1 month ago

Sorry, didn't mean to close this.

gavrant commented 1 month ago

Thanks to our talk, I have an idea for a "smarter" default camera on mesh load: for each triangle in the mesh, take its normal vector. Spice the normal up by multiplying it by the area of the triangle, so the bigger triangle is, the more it contributes to the outcome. The X, Y, Z of the result vector are basically weights for Front and Back, Left and Right, Top and Bottom camera directions respectively. So we sum up all those weights, and if any of the 6 directions significantly outweighs all the others, that's the default camera direction to apply. Using WHIntWoodWallWood01 as an example again, the result direction weights would be "extremely heavy" Back, "light" Top (thanks to a couple of smaller triangles facing up) and nothing for the other 4 directions, so we switch to Back view on its load.

This would definitely solve the issue of loading a mesh and having to rotate the camera by hand to actually see it if it's too flat or faces in the "wrong" direction, but I see 2 "but's": 1) It will be only of partial help in your particular task of automating screenshots because the real "logical front" of a model is still a rather abstract thing and a matter for the user (a human) to decide. For example, I recently checked some "hotel" static from Fallout 3 in NifSkope. It's a rectangular building, but I could tell that its "front" is the left wall simply because it has the main entrance to the building. "Front-defining" features like doorways could not be detected by simple math, it's more a job for the trendy AI or something like that. 2) I'm not a user of Blender or 3ds, so I could be wrong here, but as far as I know, they don't try to be smart about the starting camera position. Maybe there's a reason for this, like it being too confusing for the users?

Anyway, I can try and implement the idea in my fork of NifSkope in the next week.

johnsone006 commented 1 month ago

EThat would be wonderful. Honestly, it doesn't really matter too much if the default view is what I think of as the "front" or not, as long as enough of it is showing to be able to tell what it's supposed to be (surprisingly, I've seen a few meshes with names that don't really reflect what it is). Also, if you could please keep the names of the Top, Left, Flip, Perspective, front, and load view controls the same, as well as the class name of the viewport the same, that would also be super helpful.

As for your comment about blender. I'm actually not sure if it's any better than NifSkope about loading stuff. Haven't used it in a while. In any case, it wouldn't be a better choice to use than NifSkope, because if I were to use blender it would require automating the usage of drop down menus and dialog boxes. Even assuming that the dialog box wouldn't block the execution of my code, I'm trying to avoid the usage of such things because I worry the flashes might cause negative health affects for the users.

johnsone006 commented 1 month ago

Another heads up: it APPEARS as though every version of NifSkope that can be found on this GitHub page besides Dev 9 and the pre- released has some duplicate controls (I didn't check the pre-releases). I say appears because although every control returned a unique runtime id, a few of them also returned the same name, class name, bounding rectangle, localized control type, and parent Id (parent id= an arbitrary string of numbers that refers to each of the parents of a control, if any. The bounding rectangle is a property value that refers to the location and size of an item). I'm not sure if it's supposed to be like that, but if it's not, I imagine it might be slowing Nifskope down slightly.

gavrant commented 1 month ago

In any case, it wouldn't be a better choice to use [Blender] than NifSkope,

Sorry, I did not suggest to switch from NifSkope to Blender. I was just looking for an inspiration in another app which has rather similar tasks and functionality, a sort of "let's not reinvent the wheel and see how others deal with the issue at hand".

controls

What do you mean by "controls"? IControllable class and everything derived from it - Node, Shape, Property and so on? And I guess by "bounding rectangles" you mean "bounding spheres"?

johnsone006 commented 1 month ago

We are talking about different languages here. I am a c# dev, NifSkope is written in C++. But no, I mean bounding rectangles. I mean that there seems to be two toolbars named Render, as well as two of each of its children. Same thing for a few of the other controls. Tho to be fair one of the Render toolbars has a class name and the other doesn't. Either way, there are multiple controls-and by controls I mean things like toolbars, menu bars, menu items, buttons, checkboxes, that sort of thing-that seem to be in the same location with the same size that do the same thing.

gavrant commented 1 month ago

Oh, you mean UI controls! As far as I can tell from scr/nifskope.ui (which is a config file coming from/edited with Qt Creator, a visual tool from Qt framework NifSkope is based on), there are 2 controls titled "Render" - a QToolBar and a QMenu. So one is the toolbar and the other is responsible for "Render" menu. Did you find more "Render"s somewhere else?

johnsone006 commented 1 month ago

Yes, now that you mention it, there are four Renders- two menu items and two toolbars. The menu items have the same bounding rect values as each other, and same with the toolbars. I imagine this could have to do with why some of the view controls sometimes don't work the first time its invoked. Not that I've ever experienced that (as far as I know) but I remember one of the other devs for NIfSkope mentioning it to me.

johnsone006 commented 1 month ago

This keeps asking me to close the thread every time I make a new comment and there is no option to keep it open until the comment is made .Do you mind taking this elsewhere, like on discord or something?

gavrant commented 1 month ago

https://github.com/gavrant/nifskope/releases/tag/v2.0.dev9a @johnsone006 Here goes an implementation of that "smarter default camera on mesh load" idea for you to test it out. This is a release build of my fork, with a bunch of other fixes and improvements I've accumulated since the dev9 release in this repository in September 2023. WARNING: this build still has that nasty bug from 2023 that prevents meshes from saving, so keep a backup copy of your current NifSkope version to roll back to it. I'm going to fix that save bug ASAP, then will probably go with a proper release in my fork, with a changelog and version numbers updated. EDIT: I made the promised release.

This keeps asking me to close the thread every time I make a new comment

To be honest, I've no idea why this happens to you. I tried creating an issue in my private repository here, in a desktop browser (Chrome), and then adding comments to it, It did not ask me to close the issue if I pressed the green "Comment" button right under the edit box.

johnsone006 commented 1 month ago

Tentatively, this seems to fix the view problem I've been having! Before I say that for sure, I'd like to use it with SAP to take some screenshots so I can compare to the originals. But in order to do that, I'm going to need to use a different program I've created to tell me information about all the UI controls in this version of NifSkope. The reason for this is because, at the very least, my program is going to need to be able to identify the viewport because that's the thing it is taking screenshots of, not the entire UI. This program will tell me the name, the bounding rectangle, the class name, the automation ID, the property values of each control (I think of this as all the ways a UI control can be identified, such as its name or its bounding rectangle), the control patterns of each control (I think of this as ways the ui control can be interacted with), and its going to tell me what the parent of each ui control is with the exception of the root of the control tree, which in every other version of nifskope is the main window. Is this okay? If any of the controls don't have any of the above, the program will just return an empty string for that property value/pattern of that particular control. If it is not, you could just tell me how I can identify it in my program. Think of the mechanism that searches NifSkope controls as a database with filters; the goal would be to tell me enough criteria so that the database results will contain only the viewport. Also, I'd like to thank you for your help. I appreciate it a lot and I'm super excited!

johnsone006 commented 1 month ago

Actually, scratch this-I don't need to use this program yet, or even use this version of NifSkope to take screenshots. I can just dev a separate program to open a bunch of .nifs with your version of Nifskope and watch as it does it. But I am eventually going to need information to identify the viewport as well as the Top, flip, load view, front, left, and perspective controls. :)

johnsone006 commented 1 month ago

Okay, so I have news to report. As I said before, when I opened a .nif file for a mesh myself, it worked great with your version. Could clearly see the thing. But, during my first working debug session of the program I am developing to test out your version, I noticed some strange behavior. First, when my program opened nifskope, the entirety of the viewport would be black (the background color I have it set to is blue), then it would become the right color and the .nif would load. But it seemed to load the same default view as the other versions. In other words, I couldn't clearly see the thing. I let it run long enough for it to open and close a couple nifs, and they all loaded the same way. So I stopped the debug session. And then, on a hunch, I restarted it. And it worked great that time. I'm not sure what would cause inconsistency in how the .nif is loaded. I'm hoping you do (if it helps, I do have all versions of the non-prereleases of NifSkope that can be found here: https://github.com/hexabits/nifskope/releases. maybe that has to do with it? I'll screenshot the code I'm using, as well. image_2024-07-19_172857428 image_2024-07-19_172924495 Edit: So. Another thing I have noticed is that when I have my mouse somewhere in my secondary monitor, NifSkope tends to open halfway through the screens in a normal window mode. I'm not 100% sure if its your codes fault, but I think so because 1) Everything is supposed to open on a primary screen unless code changes that; 2) When I used the Dev 9 version ,it alwazys just opened on my secondary screen; and 3) as far as I am aware, the only way to move a window opened by a Process component using c# is to use one of the functions in the user32.dll. I haven't imported that .dll in this solution, so if my assumptions are correct, it can't be my code that's causing it. But I would really appreciate it if this could be resolved, because part of the goal of SAP is to enable users with more than one screen to multitask while screenshots are being taken. If NifSkope opens on the screen where the mouse is, that's going to cause all sorts of problems.

johnsone006 commented 1 month ago

To try to see if it really is your code at fault, I went ahead and tried to use your version with SAP. I learned a few things.

  1. The behavior with the window moving in the manner I described above does not happen with SAP. But! Some other weird behavior does, even when I use other versions of NifSkope. see, for some reason when I use SAP to open NIfSkope now, it opens first in what appears to be normal mode (my code specifies that it should open in maximized mode), and then it resizes to be almost maximized but not quite. It never did this to me prior to when I started using SAP with your version. So. I am wondering if it is possible that using this version might have influenced the other versions of NifSkope to behave in this manner. If so...and if you knew this was a possibility... I would have appreciated a warning about that! I know you mentioned something about the save bug, but I didn't think it would also influence how other versions of nifskope opens.
  2. The good news is, it appears that I do not need to find the viewport of this version using my other program So we can cross that off the list. Still, I would appreciate some guidance about how to get my other versions of NifSkope working correctly at this point please and thank you.
gavrant commented 1 month ago

First, SAP - what is it? To me, "SAP" is the German enterprise software company and its products, and I doubt you're talking about making .nif screenshots from accounting software 😉. Or am I wrong?

Second, there are some caveats that expect you here: 1) NifSkope is a "single process" app. That is, if you launch NifSkope.exe while another NifSkope.exe is already running, the new NifSkope.exe process sends a "open new window/file" message to that older process and exits immediately, without openning a window or loading a .nif. And it does not matter if the older process is of a different version. That's why if you have NifSkope openned of, say, version 2.0 dev 7, and then click on a dev 9 NifSkope.exe, another dev 7 windows is openned instead of dev 9. So I guess you shall check if a NifSkope.exe process is running before calling screenshots.Start(). 2) NifSkope does not care about screenshots.StartInfo.WindowsStyle = ...Maximized. Instead, when you close a NifSkope window, it saves its display, size and maximized state in the Windows registry, and when you open a new NifSkope window, it applies those saved settings to the new window. Furthemore, due to some changes in the format of the saved settings, if you lauch NifSkope's of versions older than dev 9, it resets the window settings. 3) NifSkope has its own screenshot functionality built-in. It's that "photo camera" icon in the main toolbar and in "Render" menu. It could be easier for you to work with that dialog instead of writing your own "find the viewport and make a screenshot of it" implementation.

With all that said, and taking into account that the UI in NifSkope is built on Qt framework, with its own quirks and inner workings, and that a consistent "UI API/layout" is not declared as a focus of the project (that is, any future version or fork of NifSkope may change the layout, the controls naming, etc.), have you considered a different approach to the task? For example, there's "nifly" project here on github. It's a C++ library for reading and writing .nif files, and I guess it won't be much of a problem to integrate it into a C# project (at least, I've managed to integrate it into a Python project of mine). Then for your project you would need to write only the "render and save to image file" part. This could be less time-consuming than fighting with Qt, different versions of NifSkope, etc.

johnsone006 commented 1 month ago

I have. I have decided against it because 1) given that the oldest version of NifSkope is from 2017, I don't see any reason why Hexabits might delete the versions that are working with it now. I can just update SAP (the abbreviation for Screenshot Auto Plus) as new versions are released. It's fine. Maintenance work for programs is normal, after all. 2) I do not know C++, but I do know that it is significantly more prone to resource-hogging data leaks than C#. So, C# seems to be the better pick. 3) by his own admission, my mentor is significantly more familiar with c# than C++. Given it's unlikely that I'm going to find a c++ pro that's willing to deal with my shenanigans for free like my mentor does, C# seems the better choice.

johnsone006 commented 1 month ago

As for the screenshot feature in Nifskope, I am aware of it. But I would prefer to avoid that for a couple of reasons.

  1. It opens a dialog box, which, even assuming that doesn't block the execution of my code completely, is likely going to require the focus to be on Nifskope. This is going to prevent the user from being able to multitask smoothly. Imagine typing something, and every few seconds or so you have to re click on the thing you are typing in in order to continue typing. That's what is likely to happen.
  2. The screenshot feature only offers 2 sizes, both of them bigger than the smallest option for SAP offers. Here's how that works: screenshot auto plus calculates the aspect ratio of the viewport, and then it calculates the largest possible screenshot size the user could pick and still have room for all the screenshots they want to take on the drive they chose. At that point, the user is given the opportunity to pick a size between that highest limit and the aspect ratio that is evenly divisible by the aspect ratio. It wasn't always that way, though. In the early days, I just let the screenshot size be the same size as the viewport. Then I realized that the number of screenshots at that size and number would take up 40 GB worth of space. And the # of nifs I picked to be screenshot still weren't even 10% of the total number of nifs SSE has. See what I mean?
johnsone006 commented 1 month ago

Second, there are some caveats that expect you here:

1) NifSkope is a "single process" app. That is, if you launch NifSkope.exe while another NifSkope.exe is already running, the new NifSkope.exe process sends a "open new window/file" message to that older process and exits immediately, without openning a window or loading a .nif. And it does not matter if the older process is of a different version. That's why if you have NifSkope openned of, say, version 2.0 dev 7, and then click on a dev 9 NifSkope.exe, another dev 7 windows is openned instead of dev 9. So I guess you shall check if a NifSkope.exe process is running before calling screenshots.Start().

2) NifSkope does not care about screenshots.StartInfo.WindowsStyle = ...Maximized. Instead, when you close a NifSkope window, it saves its display, size and maximized state in the Windows registry, and when you open a new NifSkope window, it applies those saved settings to the new window. Furthemore, due to some changes in the format of the saved settings, if you lauch NifSkope's of versions older than dev 9, it resets the window settings.

  1. I am so glad you mentioned that. That used to be causing me quite a lot of trouble but I found a way around it. It used to be that VS would throw a runtime exception saying the process had closed (when in reality it hadn't) if I were to open Nifskope with another window open already and then use the methods Kill, or WaitForInputIdle or the MainWindowHandle from the process class of the .Net Framework. But I have been determined to allow the user to work on other .nifs while the screenshot process is happening. So I found a workaround.
  2. That still doesn't explain the behavior of NifSkope from last night, because I never close Nifskope while it's in normal mode like that. Edit: wait. Actually I might've mispoken-I might have done that during the debugging process. Let me check and I'll report back with an edit Edit: nope. Sometimes it opens immediately to that almost maximized size, but it still is not getting completely maximized. I tried your version, the official dev 9 and dev 8. And prior to testing, I opened all the versions I have prior one by one and closed them while maximized and it still acted like that. Next edit: Would it be possible not to save the settings concerning the overall main window size and position at all? Reason being is that I'm experiencing inconsistencies in how NifSkope is opening and I don't think its being prompted by my code. Sometimes it opens in normal mode then goes to the almost-maximized-but-not-quite size, sometimes it opens in that almost-maximized-but not quite size and stays like that, and other times it opens in maximized mode properly. And I haven't been messing with NIfSkope outside of Sap today since the one time I mentioned, so I am pretty confused as to what's occurring here. But there's a reason I make nifskope maximized always-if it isn't maximized while the screenshots are being taken, but it changes sizes in a seemingly random way, the size the user chose might not fit the original aspect ratio, for one. For another, if the window moves after the bounding rectangle property of the viewport is found, the screenshot might end up being of not just the viewport anymore. That could include screenshotting part of a window in incognito mode, gavrant, and that's something I definitely want to avoid. Privacy, you know? Another edit: I have JUST noticed a change I've made to the code that probably allowed stuff from the Automation class be on the form thread. This is ill-advised generally, because it can cause conflicts that might make the program not respond. It could also be an explanation for what's occurring. I'll report back once I fix my mistakes and test. Apologies for the confusion.
gavrant commented 1 month ago

2) I do not know C++, but I do know that it is significantly more prone to resource-hogging data leaks than C#. So, C# seems to be the better pick. 3) by his own admission, my mentor is significantly more familiar with c# than C++. Given it's unlikely that I'm going to find a c++ pro that's willing to deal with my shenanigans for free like my mentor does, C# seems the better choice.

Can't guarantee that nifly is completely free of memory leaks, but in my experience, it is reliable enough to process hundreds of .nif's in one go. Also, attaching a C/C++ library to a C# project does not usually require writing a single line of code in C/C++. But, truth be told, this would require writing quite a bunch of "bindings" in C# for functions and data structures from the library, and that, in turn, would require from the coder the ability to read the library's C++ code, at least on a basic level.

2) The screenshot feature only offers 2 sizes, both of them bigger than the smallest option for SAP offers.

A workaround: the screenshot tool in NifSkope makes a snapshot of the viewport at scale 100%, then immediately your tool resizes that image file to a smaller scale. And what image format you save the screenshots in to get those "40 GB for less than 10% of the total number of nifs SSE has"?

johnsone006 commented 1 month ago

.BMP. If I remember correctly, that would've been the total size of 4736 screenshots combined. And again, gavrant, not to be argumentative but I've tried opening stuff in NifSkope using the open command. The dialog box that results blocks my code. So unless you have reason to believe that the dialog box that pops up when the screenshot button is clicked won't do that, that isn't really a workable workaround. (also, I have no idea how to attach a C++ library to a C# project anyway. Unless what you are talking about are NuGet packages; in which case, there are no NifSkope nugget packages. I've checked.)

johnsone006 commented 1 month ago

Update: I do believe that at least part of it was being caused by the fact that I allowed some of the stuff from the Automation class be on the form thread in one of SAP's classes. Again, I apologize for any trouble caused. But do you have any idea of the registry also saves what screen NifSkope was last opened on? For some reason, your version of NifSkope is opening, seemingly on its own, on my secondary screen. Edit: Looks like the answer is yes. Thank you for your help on this project, gavrant.

gavrant commented 1 month ago

.BMP. If I remember correctly, that would've been the total size of 4736 screenshots combined.

I strongly advise you to consider using JPEG instead of BMP here. I just made a quick comparison screenshoting one mesh in those 2 image formats. The results are: BMP - 4088 KB, JPEG (quality 97% which is very high) - 139 KB. That is, the JPEG screenshot takes x30 times less space than the BMP one at the cost of losing a little bit in quality (which would not matter if your goal is something like a catalogue of previews of all SKSE meshes). Another format - WEBP, would give even better results (85 KB in my test at the same 97% quality), but its support by 3rd party software (image viewers, image editors) is somewhat worse than that of JPEG.

johnsone006 commented 1 month ago

I strongly advise you to consider using JPEG instead of BMP here. I just made a quick comparison screenshoting one mesh in those 2 image formats. The results are: BMP - 4088 KB, JPEG (quality 97% which is very high) - 139 KB. That is, the JPEG screenshot takes x30 times less space than the BMP one at the cost of losing a little bit in quality (which would not matter if your goal is something like a catalogue of previews of all SKSE meshes). Another format - WEBP, would give even better results (85 KB in my test at the same 97% quality), but its support by 3rd party software (image viewers, image editors) is somewhat worse than that of JPEG. No thanks. I chose BMP because I didn't think I'd be able to accurately predict the size of any other image format ahead of time. Thanks anyways.

I think I will clarify further here. 1) if one sticks to having the screenshots be be the aspect ratio of the viewport, then my testing has revealed that several thousand screenshots can only take up less than 5% of the total storage space. Thus, I feel I have solved the issue of screenshots taking up too much space

  1. While the image quality is nice and important, .bmp file formats are uncompressed which means I can predict what size they'll be ahead of time, which is important because if the user runs out of space midway through the screenshot process it'll cause a runtime exception :) so I'll stick with .bmp. I do appreciate the research you put in, however. Thank you.
johnsone006 commented 1 month ago

So, it turns out there are exactly 21985 meshes in SSE alone. If I were to use ProcessProject to look at all of those, it would take at least 24.4 hours to do. So if you could give me some guidance about which .nifs in particular the smart camera view affects, that'd be great. Like, I am aware that we've already said that the new view loader thing affects walls. Anything else? like, for example, is there a specific type of child of the parent node I should look for? Edit 2: Alternatively, you could tell me some criteria of .nifs that it wouldn't affect, for example I've noticed a couple character skeleton meshes not show up in nifskope, I'm assuming because they aren't supposed to. Additionally, I have found a way to cut down on the initial test time. There seems to be a whole bunch-several hundred, if not over a thousand-meshes that are just character heads attached to their necks. I've added most of those to a separate folder for later testing, because 1) I have never dealt with those meshes, so I wouldn't know how they would load in previous version and 2) The ones that I have seen (approx 180ish) look fine to me. They were all facing me, and I was able to see through the places in their hair where I should. so. I'm hoping you will tell me in your next post that I don't need to test those because they wouldn't be affected by the new changes, but if not, I'll test those last.

johnsone006 commented 1 month ago

Update on the registry setting thingie: are you certain you did not program it in a way that would cause it to reset in any way after a while? It has started opening in normal mode for some inexplicable reason. Series of events:

  1. I opened your version of NifSkope manually. It opened in maximized mode and on the correct screen by default. Then, I closed it.
  2. I began testing .nifs with your version of NifSkope using ProcessProject.
  3. I stopped the debug session after a bit to go do stuff. I stopped it after a nif had closed, but before the next one opened.
  4. I started using ProcessProject again, and for some reason, it began opening in normalized mode again. The only other people here are people that do not mess with my machine except to put it to sleep (which they didn't do today) so it can't be that someone opened nifskope and then closed it while it was in normal mode while i was gone. Also, the assembly packages for System.Windows.Automation aren't even in this solution, so it can't be that either.
johnsone006 commented 1 month ago

Update: Since I am too impatient to wait for you to write back, I got an idea. I'm going to write a separate program that will separate all the .nifs that have BSShaderTextureSet nodes from the ones that don't. Because I'm assuming the latter isn't supposed to be visible in game anyway.

johnsone006 commented 1 month ago

Update: the program I'm using to sort the .nif files is working as we speak. I anticipate it'll be done by the time I wake up tomorrow morning. I'd be happy to post the code on here for you to use, if you'd like. Same for NifSkopeInvoker2 (the program i mentioned earlier, that I used to determine that some of the controls in Nifskope are seemingly duplicates).

johnsone006 commented 1 month ago

Update, in case you want to know: I am....struggling. So. NifSorter (the program I am developing to sort the .nif files) is working significantly more slowly than I anticipated, and I can't tell if it's because it needs to look through several automation elements to check if the file has a BSShaderTextureSet or if I have stopped avoiding putting automation stuff in the form thread or both (my mentor recently told me that I had misunderstood the ramifications of doing that-allegedly, it only causes problems if the dev uses things like the FindAll method on their own controls on the form thread. But idk... SAP never causes NifSkope to freeze or not respond, but NifSorter does. I suspect, several times. I woke up this morning to realize I had left it running for at least 4-5 hours and yet it still only managed to sort 200-some nifs. In comparison, SAP has been able to take and save 4736 screenshots in a single night and it was slowed down by pauses for each screenshot. NifSorter doesn't have any pauses implemented except for the one that happens in order for Nifskope to have a chance to return a main window handle that's not an IntPtr.Zero.

Another thing to consider is I've never had to iterate through any of the controls in the dockable windows in Nifskope so that could be adding significantly to the time as well. At this point, I would just give up by sucking it up and watching all the nics open, but I need to figure out the automation thread thing. Because if the strange behavior the other day wasn't caused by that,something else caused it. I'm trying really hard to prevent ppl from getting seizures here, but it's kinda hard when my code doesn't act consistently

gavrant commented 1 month ago

Sorry, I'm not familiar with the tools you're using in your project, like SAP, so I'm of little help here.

So if you could give me some guidance about which .nifs in particular the smart camera view affects, that'd be great.

The "smart(-er) view" is a purely geometry solution. That is, whether it's applied to a mesh or not, is decided on the direction the majority of the triangles of that mesh are facing. And with the exception of skinned meshes (actor skins, armors), there are no hard rules for modelers how to orient their models. And mesh files store no info about what to consider their "true front". In other words, it's quite random. For example, I won't be surprised if not all wall meshes face Back camera, some could easily face Front or even Left/Right. And one thing I found in the past few days: the "smart view" does not work well for Skyrim weapons (it goes for the default Front, though Bottom would be the correct camera for them).

Update on the registry setting thingie: are you certain you did not program it in a way that would cause it to reset in any way after a while? ... I began testing .nifs with your version of NifSkope using ProcessProject.

Does your code still call Kill() to terminate NifSkope? My guess is: Kill() force-terminates the process, preventing it from doing its "on close" ("normal exit") stuff, and "on close" is when NifSkope saves its windows settings.

johnsone006 commented 1 month ago

I really appreciate you, gavrant. Yes, I was using Kill. But for SAP, I use the EndTask function from the user32 .DLL as part of the code that allows users to have multiple nifskopes open. But I am guessing that would still mess with the process of saving stuff to the windows registry because it also forces a close the way I am using it. It takes forever for it to close on its own otherwise (like, for example, by using the Close method from the process class).

I think I want to think for a bit about how I want to tackle this issue. Update on NifSorter: at this point, I'm like 90% sure that the bulk of the time that's being taken is accounted for by the code that searches for the BSShaderTextureSet due to that element having SOO many siblings. I tested one nif while a breakpoint in the code was put at the beginning; finding the BSShaderTextureSet took 29 of the 39 seconds it took to sort that nif. So I'm relatively sure that no matter how I implement this code, sorting it will take longer than just biting the bullet and going through all of them. I might still release it if people are interested, I just probably am not going to use it for this project.

johnsone006 commented 1 month ago

So you remember when you suggested that I use open command? I think I'm going to have to do that. I've already worked out the code to implement that functionality using SendKeys.SendWait. Next step is to replace SendKeys.SendWait with SendMessage() (from the user32 .dll). If you know of a way to open .nif files in the same window without having to make the open dialog appear, now would be a really fantastic time to mention it please and I would appreciate it. Otherwise, my doctor told me that between 5-10 flashes per second can cause seizures for someone with epilepsy; I'll just slow down my code to account for that.

johnsone006 commented 1 month ago

Update on NifSorter: you may oooh and aaahh at my genius, because I found a way around the issue I was experiencing (I'm kidding about the ooh and ahh part, lol). It's much faster now.

johnsone006 commented 1 month ago

Anyways, gavrant, if you ever feel the need to take this conversation elsewhere, I have my own channel for screenshot auto plus in this discord: https://discord.gg/eEU5rGdQ In the meantime, I do have a couple more questions:

  1. Can you please send me a list of shortcuts for the functions in Nifskope? You see, the only way I have been able to get the open file dialog in Nifskope open automatically using dev 8 is either 1) using SendKeys.SendWait to send the alt+f and then the enter message or 2) finding the file menu, invoking it, and then using send message to send the down arrow key and then the enter key. Unfortunately, in either case, it seems that focus needs to be on NifSkope in order for it to work. So I might need to create my own hot key for the open menu item myself. Unless you have objections?
  2. I think you were right about the Kill method because the cause of the strange behavior before. But that begs the question: now that I am not using it anymore do I need to do anything to fix the damage besides not use it?
gavrant commented 1 month ago

1, As far as I know, the "list of shortcuts" matches the shortcuts displayed in the menus - Ctrl+N for new window, Ctrl+S to save and so on. The only thing that has its own keyboard controls in the viewport, but that's mostly keys for moving around in the viewport, like WASD, There's one more option to open a file though - NifSkope supports dragging and dropping a file from Windows file browser to its main window, you can try to emulate that,

  1. Closing the main window by emulating an Alt+F4 press or a click on the "x" in its titlebar + waiting a bit should be enough for NifSkope to end its work properly.
gavrant commented 1 month ago

If you still use the version of NifSkope with the "smart view" I uploaded here 2 weeks ago as nifskope_2_0_dev9_gavrant1.zip, please replace it with this: https://github.com/gavrant/nifskope/releases/tag/v2.0.dev9a . It got a handful of tweaks since then.

johnsone006 commented 1 month ago

If you still use the version of NifSkope with the "smart view" I uploaded here 2 weeks ago as nifskope_2_0_dev9_gavrant1.zip, please replace it with this: https://github.com/gavrant/nifskope/releases/tag/v2.0.dev9a . It got a handful of tweaks since then.

YAY!! More good news: Today, I managed to cache the open menu item in the official NifSkope Dev 9 so that way, hopefully I'll be able to multitask while NifSorter is running without causing any runtime exceptions. But boy howdy, it was a GIANT pain in the butt to find. TLDR: the program I use to find Nifskope elements, NifSkopeInvoker2, detected 7 elements that were expandable in the subtree. NifSorter, however, detected one, even tho I used the exact same code to find the elements. And that one was NOT the direct ancestor of the open menu item. Plus, the parents of the open menu item didn't make a lot of sense to begin with. Went something like this: MainWindow

I'm kinda curious as to why it's not: MainWindoe -Menu called File -open menu item

But in any case, I managed to cache the open menu item eventually by using sending the access key of the file menu item to the window and then assigning the open menu item to an automation element using its coordinates . Speaking of which, i still need your permission to use the new version of Nifskope with NifSkopeInvoker2

gavrant commented 1 month ago

The layout of UI controls in NifSkope is the inner workings of Qt. Considering that Qt is a crossplatform framework that must do its job on a multitude of OSes, it's no wonder that the internal structure of the UI it generates may look weird and overcomplicated. And the 20 years-long history of NifSkope's own src/ui/nifskope.ui could also contribute to that.

Speaking of which, i still need your permission to use the new version of Nifskope with NifSkopeInvoker2

If we are talking about personal use, feel free to use my version, of course. But if we're talking about publishing and distributing it as part of a package/another app, it's a more complex issue: you would need to monitor my releases; I would need to keep in mind backward compatibility so my changes would not break your app; if the ideal scenario happens and my changes get into the "official" version of NifSkope, then it would be better if you migrate to that version; and so on, and so forth.

johnsone006 commented 1 month ago

I'm glad you brought that up; I was wondering what credit, if any, I need to give to NifSkope for it being used by SAP. I'm happy to send you the source code in private if it is necessary to answer this question, but for now, I'll just give a basic overview.

So there are three classes in SAP; The first class is the form where the user picks the locations of the .nif files, the soon-to-be .bmp files, tells the program if the input file has subdirectories they want to search, and also which version of NifSkope they want to use. They basically pick the version of NifSkope by navigating to the folder that has the executable in a file browser dialog, and from there the program adds the final part of the path (the executable).

The second class is where the preparation for the screenshot process happens. It opens NifSkope (hopefully in maximized mode) in order to find the viewport and get its size, and it also gets the height of the area above the viewport (this value helps the next form show up correctly on the screen).

The third class is where the actual screenshotting happens. It's a form that is designed to sit on the side of the screen, above the windows that the user (hopefully) decided to dock on the left .Its important to have something docked there because that gives SAP a place for its controls without ever obscuring any part of the viewport. This class is also the class that uses the hotkeys for the view controls, if they chose to load the .nif in anything other than the default view.

johnsone006 commented 1 month ago

Update on the new version of NifSkope that you linked me: As great as all those improvements sounds, something about this doesn't save the settings like the other NifSkope version isn't consistently saving settings like the others. I know, I know- a few days ago, I was complaining about why I wasn't able to set the window style of nifskope. But as I said before...Consistency. The first couple times I opened this new version with the new app I'm developing (an improved version of NifSkopeInvoker2), it opened in normalized mode. Now, after I opened this version and arranged the tabs myself then closed, it behaved like this: HowIClosed HowItOpened The first is how I set up the UI of NifSkope myself, the second is how it opened on its own with the app afterward. Speaking of this new app. I found a significantly better way of discovering nifskope's hierarchy than what I came up with before. Yay!

gavrant commented 1 month ago

Update on the new version of NifSkope that you linked me:

The NifSkope on the screenshots right above is not one of my releases from here: https://github.com/gavrant/nifskope/releases. Judging by the title of the main window, it could be the old "official official" Dev 9 release of Hexabits from September 2023.

I'm glad you brought that up; I was wondering what credit, if any, I need to give to NifSkope for it being used by SAP. I'm happy to send you the source code in private if it is necessary to answer this question, but for now, I'll just give a basic overview.

I'm still in the dark about, so to speak, the "business plan" for your project: how, where and to whom you're going to distribute it? Judging by "send the source code in private", it's not an open-source, free-for-all project? Possibly with money involved? In any case, many projects/apps/libraries on the Internet have a license that tells you what you can and can't do with them. For NifSkope, it's https://github.com/hexabits/nifskope/blob/develop/LICENSE.md , and to quote the "About NifSkope" dialog in the app itself, "NifSkope is free software available under a BSD license" (probably this one: https://en.wikipedia.org/wiki/BSD_licenses#3-clause_license_(%22BSD_License_2.0%22,_%22Revised_BSD_License%22,_%22New_BSD_License%22,_or_%22Modified_BSD_License%22) ).

johnsone006 commented 1 month ago

Okay.

  1. What leads you to believe that it wasn't your version of NIfSkope? Because I'm certain that it was. The program I used to open it relied on the file path of your version to open it.
  2. No, SAP will never be for commercial purposes. I simply just didn't want the actual code to be released to the public before it's working to my satisfaction. Don't want someone to steal it and then take credit for the many, many, many hours I've spent on this project, you see. And it will be available to anyone.
gavrant commented 1 month ago

What leads you to believe that it wasn't your version of NIfSkope? Because I'm certain that it was. The program I used to open it relied on the file path of your version to open it.

If it's the latest version, the title bar should say this: 9c

johnsone006 commented 1 month ago

Well, it doesn't. Granted, it could be my fault- I originally to literally replace the original version you gave me with this one by extracting the new one (the one you gave me 3days ago) to a new folder and then pasting the folders contents into the folder that contained the version you gave me 2 weeks ago. Didn't work out too well-when i tried to open it, I got a warning saying something along the lines of windows protected my computer.So i deleted that folder and reextracted the files, and nifskope did open, but it behaved in the way I mentioned before.

johnsone006 commented 1 month ago

Just downloaded the most recent release- the titlebar said what it was supposed to say. Thank you for your guidance thus far.

johnsone006 commented 1 month ago

It did it again ![image](https://github.com/user-attachments/assets/6bdbe6af-05d0-44ae-b478-c2f6f8ab2b93 but i'm starting to think this might be my doing. When I close it while its minimized, how does that affect the registry settings? Edit: mystery solved. My fault. It still opened in minimized mode, however, even though I've closed it in maximized.... When I opened it manually again and then closed it maximized, ElementFinder opened it maximized. I think I just need to open and close it in maximized mode myself a couple times to get the registry settings to stick.

johnsone006 commented 1 month ago

I have another suggestion. So, if you'd be okay with humoring me... The TLDR of my suggestion: Make the elements (we'll call them hiddenElements) that are contained in the header window be children of the elements you need to click on in order to reveal the hidden elements. At the moment, all the elements you see in the header window are just children of the tree. Bonus points if you give the to-be parents of the hidden elements ExpandCollapsePatterns. Usage scenario: it would make the objective of sorting .nifs based on whether or not they have textures easier. Atm, NifSorter makes that determination by taking 1 screenshot of the viewport for every view, then for each of those bitmaps, iterating through each pixel to compare the color of it to the background color of the viewport. Once it finds a pixel that is a different color than the background color, the method returns true; if it never finds a pixel like that, it returns false. But if I could simply just get a list of, for example, the hidden elements that are children of block types, that would simplify things a bit. As well as the added bonus of being able to sort by other values too, though I'm not sure how useful that'd be. Because I'm not familiar with the different node types. Plus, wouldn't it just make sense for elements that aren't visible until something gets clicked to be children of said clicked elements?