Repository: assimp/assimp

Issue #2082: Memory Leak in Importer
Created: 2018-08-04T18:34:57Z
Hi
Recently I use Assimp to import my model in OpenGL project.
However, it has a memory leak in the .obj importer code. I have no idea why this happens.
int main()
{
int abc = 0;
Assimp::Importer imp; // if comment this then no memory leak...

#if defined _DEBUG
_CrtDumpMemoryLeaks();
#endif // _DEBUG
    return 0;

}
Detected memory leaks!
Dumping objects ->
{713} normal block at 0x000002140AF0F320, 16 bytes long.
Data: <                > 00 8C F0 0A 14 02 00 00 00 00 00 00 00 00 00 00
{712} normal block at 0x000002140AF001D0, 48 bytes long.
Data: <                > D0 01 F0 0A 14 02 00 00 D0 01 F0 0A 14 02 00 00
{711} normal block at 0x000002140AF08C00, 24 bytes long.
Data: <                > 20 F3 F0 0A 14 02 00 00 D0 01 F0 0A 14 02 00 00
{710} normal block at 0x000002140AF08FC0, 32 bytes long.
Data: <                > 98 05 C4 0C F9 7F 00 00 00 8C F0 0A 14 02 00 00
{709} normal block at 0x000002140AF090E0, 32 bytes long.
Data: < P              > A0 50 C1 0C F9 7F 00 00 00 8C F0 0A 14 02 00 00
{708} normal block at 0x000002140AF11790, 296 bytes long.
etc...
Windows 10, visual studio 2017 and Assimp 4.1.0

Duplicate issue #5002: Bug: Half-Life MDL broken bone hierarchy
Created: 2023-03-09T12:59:11Z
Describe the bug
Model test/models/MDL/MDL (HL1)/man.mdl has hierarchy like this:
<MDL_root>
├╴<MDL_bones>
│ ├╴Root
│ ├╴Pelvis
│ ├╴RightLeg
│ ├╴LeftLeg
│ ├╴Spine
│ ├╴RightArm
│ ├╴LeftArm
│ └╴Neck

But the bone nodes have "MDL_root" as their parent instead of the correct "MDL_bones". In addition the hierarchy doesn't appear to match what's actually in the file. It looks like this:
0 "Root" parent -1
1 "Pelvis" parent 0
2 "RightLeg" parent 1
3 "LeftLeg" parent 1
4 "Spine" parent 1
5 "RightArm" parent 4
6 "LeftArm" parent 4
7 "Neck" parent 4

The hierarchy looks to have been flattened which would lead to wrong rendering of the model.
The code is here:

  
    
      assimp/code/AssetLib/MDL/HalfLife/HL1MDLLoader.cpp
    
    
         Line 487
      in
      f3767a4
    
  
  
    

        
          
           if (pbone[i].parent == -1) { 
        
    
  


It incorrectly assigns the scene root scene_->mRootNode as the "Root" bone parent instead of "MDL_bones" node bones_node.
The child bones appear to get the correct parent but they are still in the root bone children array instead of being moved to their actual parent's children.
To Reproduce
Steps to reproduce the behavior:
#5001 fails unit tests because I added validation for the hierarchy. Grab that branch and run unit tests.
Expected behavior
Hierarchy is properly formed and the unit tests pass with the new validation.
@malortie I think you wrote this code. Can you fix it?

Closing comment (2023-03-12T22:09:24Z): I created a new branch with changes that fix the issues. The branch was created from #5001 and includes turol's changes in ValidateDataStructure.cpp. I can also create a separate PR with only HL1 MDL importer changes if preferable.
Changes made to HL1 MDL Importer code
Hierarchy is now constructed to follow a tree-like structure (parent/child) instead of flattened. While reviewing the changes, I found that StudioMDL (the tool used to compile HL1 models) is able to compile SMD files that have multiple roots. The importer code now checks for root bones (parent: -1) and builds the hierarchy for each of them. They are now parented to the main 'bones' node <MDL_bones> instead of scene->mRootNode, as previously explained.
I made a new model multiple_roots.mdl to use for testing the new hierarchy and to ensure correct loading of MDL models with multiple root bones. The SMD source files are available here.
Changes made to unit tests
1 - A new test was added - checkBoneHierarchy. Uses multiple_roots.mdl.
2 - The test emptyBonesNames was adjusted to work with the new hierarchy.
3 - Various tests code were reworked. expect_named_children() was removed and tests that were using it now use get_node_children_names(). The new function is more flexible because it allows collecting node names that are under a specific hierarchy level. This is useful in tests checking only bodypart names but not their submodels. In addition, expect_named_children() was containing assertions checks. This is now decoupled with the new structure.
Helpers
Various utilities were made/used to help test further. A separate branch has been made and contains additional utilities. The branch includes all changes from validate_parents, including changes for HL1 MDL importer.

Note: CMake targets were added for each utility.

assview
Used to help visualize 3D MDL files. Only tested in Win32/x86.

Adapted from assview.c.
FreeGlut 3.0.0 was used and must be installed manually to samples/freeglut.
Changes were made to display line segments to be able to view bones.
It does not handle loading embedded textures, so they won't appear in the viewer. Given no change was made to texture processing, this is likely not needed.

memory_leak_check
Used to check for memory leaks after importing a model and disposing the scene.
Models tests
Each model below was loaded in assview. HL1 game specific models were additionally loaded in HLAM to check correctness of their bone structure.
Half-Life 1 game models:
valve/models/
  player/
    barney.mdl
    gina.mdl
    gman.mdl
    gordon.mdl
    helmet.mdl
    hgrunt.mdl
    recon.mdl
    robo.mdl
    scientist.mdl
    zombie.mdl

  barney.mdl
  bullsquid.mdl
  hgrunt.mdl
  islave.mdl
  player.mdl
  scientist.mdl
  tentacle2.mdl
  v_9mmar.mdl
  w_9mmar.mdl
  zombie.mdl

Assimp tests models:
test/models/MDL/MDL (HL1)/
  duplicate_sequence_groups/duplicate_sequence_groups.mdl
  unnamed_sequence_groups/unnamed_sequence_groups.mdl

  alpha_test.mdl
  blend_additive.mdl
  chrome_sphere.mdl
  duplicate_bodyparts.mdl
  duplicate_sequences.mdl
  duplicate_submodels.mdl
  man.mdl
  multiple_roots.mdl
  sequence_transitions.mdl
  unnamed_bodyparts.mdl
  unnamed_bones.mdl
  unnamed_sequences.mdl

Memory leaks
memory_leak_check was used to ensure no memory leaks were introduced with the new changes.
Memory leaks as such were found:
Detected memory leaks!
Dumping objects ->
{170} normal block at 0x007CE7E8, 8 bytes long.
 Data: <xF z    > 78 46 12 7A 00 00 00 00 
{169} normal block at 0x007CE238, 8 bytes long.
 Data: < E z    > 8C 45 12 7A 00 00 00 00 
{168} normal block at 0x007CE468, 8 bytes long.
 Data: < E z    > A8 45 12 7A 00 00 00 00 
{167} normal block at 0x007CE7B0, 8 bytes long.
 Data: < E z    > 20 45 12 7A 00 00 00 00 
{166} normal block at 0x007CE900, 12 bytes long.
 Data: <  |   |     > 00 E9 7C 00 00 E9 7C 00 CD CD CD CD 
{165} normal block at 0x007CE6D0, 8 bytes long.
 Data: <HE z    > 48 45 12 7A 00 00 00 00 
{164} normal block at 0x007C9C38, 28 bytes long.
 Data: <8 | 8 | 8 |     > 38 9C 7C 00 38 9C 7C 00 38 9C 7C 00 01 01 CD CD 
{163} normal block at 0x007CE388, 8 bytes long.
 Data: <<E z    > 3C 45 12 7A 00 00 00 00 
Object dump complete.
'memory_leak_checkd.exe' (Win32): Loaded 'C:\Windows\SysWOW64\kernel.appcore.dll'. 
The thread 0xdcfc has exited with code 0 (0x0).
The thread 0xa6e4 has exited with code 0 (0x0).
The program '[27868] memory_leak_checkd.exe' has exited with code 0 (0x0).

These occur even when ASSIMP_BUILD_ALL_EXPORTERS_BY_DEFAULT and ASSIMP_BUILD_ALL_IMPORTERS_BY_DEFAULT are disabled, and even when no scene is loaded. These could probably be related to #2082 . During testing, both were disabled and only ASSIMP_BUILD_MDL_IMPORTER was enabled.
I also loaded each aforementioned model and checked to see if additional blocks would show up. So far all block numbers appear to remain consistent and no additional block occur. The only notable things changing occasionally are the numbers in the span [163, ... , 170], sometimes starting at a higher number i.e. [164, ... , 171], however the span remains the same, so probably due to 2082 and not the HL1 MDL importer. This behavior also occurs in x64.
Unit tests execution
Unit tests were run on Debug, MinSizeRel, Release, RelWithDebInfo.

Win32: All tests pass, but aiVector2DotProductTest and aiVector3DotProductTest occasionally fail in Release and RelWithDebInfo.
x64: All tests pass.

Compiling
Compilation was tested in Win32 and x64, each on Debug, MinSizeRel, Release, RelWithDebInfo.
Everything builds correctly, as for the HL1 MDL importer changes.

Note: assview is set to work with the Freeglut x86 libraries and will only compile on Win32.
Note: Building only with ASSIMP_BUILD_MDL_IMPORTER enabled and all the other importers/exporters disabled will fail because MDLImporter uses LookupNormalIndex, which is defined in MD2Loader.cpp.
This can be solved by moving the function to a separate file shared between MD2 and MDL.


@turol Please, can you check the changes and confirm if it works ? Optionally, you may use the helpers in validate_parents_utils branch.