Interkarma / daggerfall-unity

Open source recreation of Daggerfall in the Unity engine
http://www.dfworkshop.net
MIT License
2.72k stars 330 forks source link

Replacement OBJ models invisible on dungeon map after loading save #1995

Closed XJDHDR closed 3 years ago

XJDHDR commented 3 years ago

Describe the bug While modding DFU, I noticed that every model I created was invisible on the explored dungeon map after loading a save and only became visible again after I walked to those invisible models and re-explored the area. However, these re-explored models would disappear again after loading any saves after that as well.

Recently, I discovered that this only happens if the replacement models are OBJs. As a test, I replaced a staircase model that I was using an OBJ model for with an FBX model and loaded it into my mod. After loading the save, the staircases using that model were now visible on the dungeon's map.

So there is something about the way DFU is deciding what dungeon pieces to reveal on the map while loading a save that breaks if the piece uses an OBJ model. I'm wondering if this is a similar issue to one Petchema fixed in his pull request #1988. That this part of DFU was only designed to look at the root of the prefab and not at it's children? I've attached a save and the two staircase models to help reproduce this: Save.zip Models.zip

To Reproduce Steps to reproduce the behavior:

  1. Load the OBJ model into your game however you prefer. Make sure you rename the OBJ prefab to "61018.prefab".
  2. Load the save game. Notice how the staircases in the corridor in my screenshot is not visible on the dungeon map even though that corridor was explored.
  3. Load the FBX model into your game however you prefer.
  4. Load the save game. Notice that the staircase pieces are now visible on the map. Please note that if you notice that the model doesn't become transparent, Petchema has already submitted a pull request that fixes this so don't worry.

Screenshots Here are some screenshots of what I am talking about. Please don't worry about these models being completely white. It is a separate issue that I've already reported elsewhere. This first one is where I'm only using OBJ replacement models. The corridor across the middle of the picture is supposed to be fully explored but pieces of it are missing. OBJ hidden corridor pieces without FBX test

This second one shows what happened after I replaced the staircase model in that corridor with an FBX model. You can see that now the staircase pieces of that corridor are visible after loading my save. OBJ hidden corridor pieces with FBX test

Desktop (please complete the following information):

Nystul-the-Magician commented 3 years ago

this is a consequence of how discovery state saving and storing works - the gameobject name is used for saving discovery state and restoring it. I bet the model name changes when it gets replaced so the algorithm for restoring does not find it and cannot restore it

XJDHDR commented 3 years ago

@Nystul-the-Magician The problem I see with your explanation is that the model name is not changing in between these screenshots. The staircase in all cases is named "61018". The corridor corner model is still named "61004". To successfully change the model's name, I would have to change the relevant data in the Dungeon Block as well. I also don't see why failing to restore discovery state would only affect OBJ models and not the second screenshot where I used an FBX model.

Also, I just did another test and took this screenshot. After I configured the editor to load my replacement OBJ models, I loaded my save then ran through the corridor in the screenshot. After checking the map, all of those missing models were revealed. I then saved the game. Then I closed and restarted the game and then loaded the save I just made without changing the mod configuration. I then opened my map and saw what was in the screenshot. As you can see, the same corridor models are still missing even though their modded state should have been saved. Modified model discovery data test

I've already found a number of other bugs in DFU related to replacing models using the Wavefront OBJ format. I'm convinced that this report is just another example of that.

Nystul-the-Magician commented 3 years ago

could it be that the replacement model has an extra gameobject container (an extra level) in it? so gameobject hierarchy is different? if not then I cannot think of a reason why it should fail :/

XJDHDR commented 3 years ago

@Nystul-the-Magician Here are screenshots of the prefab for the OBJ version of my staircase model. It consists of the root prefab named "61018" and a child of that named "default". I'm not sure why OBJs are arranged this way but that's just the way they are. OBJ root OBJ child

Opening the prefab in a text editor, I see that there are two gameobjects defined:

%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &1161699233836534
GameObject:
  m_ObjectHideFlags: 0
  m_CorrespondingSourceObject: {fileID: 0}
  m_PrefabInstance: {fileID: 0}
  ...
  m_RootOrder: 0
  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &1280155942608658
GameObject:
  m_ObjectHideFlags: 0
  m_CorrespondingSourceObject: {fileID: 0}
  m_PrefabInstance: {fileID: 0}
  m_PrefabAsset: {fileID: 0}
  ...

61018.zip

Nystul-the-Magician commented 3 years ago

maybe you could get rid of the default subobject, by changing either the hierarchy of geometry in your 3d modeling software before exporting the obj file or by changing export settings in your 3d modeling software (e.g. Blender obj export setting "Object as" allows options "OBJ Objects" and "OBJ Groups" - both generating different hierarchy there are also the import settings for obj assets - click on asset and in unity inspector you will get the settings. why do you use .obj files instead of .fbx - asking just out of curiosity, because unity supports fbx better than obj in my experience

XJDHDR commented 3 years ago

I haven't done anything special to my model. The entire mesh is a single object in Blender and the same object I imported from the original DAE exported by DF Modelling. No export setting I tried got rid of the children. When I ticked only "OBJ Groups", all it did was change the name of the child to "model61018-node_model61018" instead of "default". This didn't change the problem.

The two main reasons I use OBJs is because they are smaller than exporting the same model as an FBX and because FBXs are saved in a binary format whereas OBJs are plain text. Sure, there are some shortcomings to OBJs verses FBXs (e.g. no support for rigging or animation) but these don't apply to any of the modelling I've done so far.

I'm not sure about Unity's level of support for OBJs but in my experience, they are doing the job and the only issues I've had so far were caused by DFU rather than Unity. Since my models work as they should outside the dungeon map, this leads me to believe that this is the case here as well.

Nystul-the-Magician commented 3 years ago

can you send me the .obj model file - would like to take a look and see why it gets imported into unity this way ;) from dfunity point of view it is rather a design limitation than a bug actually imo ;) but I can't think of an easy way to fix this. dfunity cannot just guess how a replacement model's hierarchy will look like and what names are given to it. It is the responsibility of the replacement model to have same name and model hierarchy ;)

nevertheless the issue is a minor one since rediscovering will make it show up again

XJDHDR commented 3 years ago

@Nystul-the-Magician You can find my model in the models.zip attached to the OP.

Nystul-the-Magician commented 3 years ago

ah sry, overlooked this ;) thank you

Nystul-the-Magician commented 3 years ago

looks like unity's .obj-importer creates this "default" sub-gameobject, unity's fbx importer does not...

XJDHDR commented 3 years ago

@Nystul-the-Magician

dfunity cannot just guess how a replacement model's hierarchy will look like and what names are given to it

I'm not sure I agree with you here. You don't need to guess these for an OBJ: We know exactly what they look like. The root of the model is named after the model it's replacing and the meshes and materials are contained in at least one direct child.

I am curious though. What part of the code is responsible for defining discovered models in a dungeon after the player loads a save? I also wonder how relevant a recent pull request Pango made is to this. I had found a different issue where OBJ models couldn't turn transparent on the dungeon map. Pango fixed it by making the meshRenderer variable an array then iterated through every element in that array, calling the UpdateMaterialsOfMeshRenderer() function on each one.

rediscovering will make it show up again

Only until the next time the player loads a save, at which point all discovered OBJ replacements will disappear and have to be re-explored once again.

Nystul-the-Magician commented 3 years ago

@Nystul-the-Magician

dfunity cannot just guess how a replacement model's hierarchy will look like and what names are given to it

I'm not sure I agree with you here. You don't need to guess these for an OBJ: We know exactly what they look like. The root of the model is named after the model it's replacing and the meshes and materials are contained in at least one direct child.

imo, it would be the responsibility of the object replacement code that should handle this stuff, the automap does not know anything if the model was loaded as obj or fbx or whatever, or how model hierarchy does look like, it cannot guess anything here without breaking stuff, when for whatever reason things are different (cause of other import format, etc.)

I am curious though. What part of the code is responsible for defining discovered models in a dungeon after the player loads a save? I also wonder how relevant a recent pull request Pango made is to this. I had found a different issue where OBJ models couldn't turn transparent on the dungeon map. Pango fixed it by making the meshRenderer variable an array then iterated through every element in that array, calling the UpdateMaterialsOfMeshRenderer() function on each one.

function SaveStateAutomapDungeon in line 2250 of Automap.cs saves the state function RestoreStateAutomapDungeon in line 2464 of Automap.cs file restores the automap: in line 2512 UpdateMeshRendererDungeonState is called in the loop for every separate model if it before manages to identify the model by name and hierarchy - the point where this fails for your object because of different hierarchy

rediscovering will make it show up again

Only until the next time the player loads a save, at which point all discovered OBJ replacements will disappear and have to be re-explored once again.

so if I load the save provided, and I rediscover the replaced model, save it to a new save and load this newly created save it is hidden again?

What is the easiest way to test this? How do I replace the model with id 61018 so that it is automatically replaced when the save is loaded? I don't want to replace it manually after the save got loaded, since this would not behave the same way as when using the automated model replacement functionality - I am asking since I never used the model replacement before and don't know how to do it ;)

XJDHDR commented 3 years ago

imo, it would be the responsibility of the object replacement code that should handle this stuff

I agree with you there. The modding system hypothetically should be presenting everything else data that is indistinguishable from vanilla.

in line 2512 UpdateMeshRendererDungeonState is called in the loop for every separate model

Thanks, I was looking in the completely wrong location.

so if I load the save provided, and I rediscover the replaced model, save it to a new save and load this newly created save it is hidden again?

Precisely. I found this out while writing my first reply to you and made a note of it there.

What is the easiest way to test this? How do I replace the model with id 61018 so that it is automatically replaced when the save is loaded? I don't want to replace it manually after the save got loaded, since this would not behave the same way as when using the automated model replacement functionality - I am asking since I never used the model replacement before and don't know how to do it ;)

I would recommend following TheLacus' instructions for creating a virtual dfmod. You can find them here under the "Debug a mod" section. You also need to make sure all prefabs have the same name as the model they are replacing (i.e. remove the " OBJ" suffix).

If you want something easier than that, I would suggest downloading my mod from here. I would suggest the 2021.01.09 version as I think I accidentally put the FBX version of my staircase model in the later version.

In either case, just make sure the mod is activated in the mod menu before testing.

Nystul-the-Magician commented 3 years ago

btw, I managed to create prefabs out of .obj files without the child go when opening the .obj file in project window, expanding the file and drag out the inner model into scene hierarchy afterwards you can create a prefab out of it - unfortunately this needs these manual steps to be done...

XJDHDR commented 3 years ago

Thanks, that's good to know. Requires a bit more work to set up though in that materials need to be manually assigned this way (not a problem for me since I unassign those materials anyway and use Runtime Materials instead) and the prefab needs to be renamed correctly.

This also won't work with OBJs that contain multiple Game Objects. An example of this is if someone ticks the "Objects as: Material Groups" option when their model uses multiple materials. I don't do this but maybe someone out there does.