multiple materials for dforce hair

Issue #1050 resolved
Alessandro Padovani created an issue

daz studio 4.15.0.30, blender 3.2.0, diffeomorphic 1.6.2.1041

It may happen that dforce hairs get multiple materials. In this case the materials are imported fine, but the make hair tool doesn’t work fine, that is, a single material is assigned to the hair.

An example is the soft curls in the Josephine bundle. Test scene included curls.duf. Unfortunately I don’t have many dforce hairs and I didn’t find another case yet, so Thomas let me know if you can get the soft curls or I’ll see if I can find something else.

Personally I didn’t find any way to assign multiple materials to sbh hairs, so this seems to be possible only with dforce hair and I can’t do a simple example with sbh.

https://www.daz3d.com/dforce-soft-curls-for-genesis-8-and-genesis-3-females

steps:

  1. import the test scene curls.duf
  2. make hair with multiple materials

note. This specific hair takes a long time to convert, even if I don’t use the resize option. In edit mode it’s about 800k edges and it takes 4 minutes here to convert to particles. Then if I use the resize option it takes forever and I have to quit blender.

note. It would also be useful if “select strands by size“ could work with line hair too, since it now works only with sheet hair. Also may be “select strands by length“ would be a better name to distinguish from “select strands by width“.

Comments (31)

  1. Alessandro Padovani reporter

    note. I noticed that “separate loose parts“ is not available for line hairs. So may it be that “separate loose parts“ could speed-up line hairs too, the same as it does for sheet hairs ?

    note. May be this was already requested or suggested. If possible it would be useful to have a progress bar for make hair and possibly a “abort“ button to avoid quitting blender when it takes too long.

  2. Thomas Larsson repo owner

    A single material is assigned because you have unchecked Multi Materials. If you enable it, a separate particle system is generated for each material and each strand length. The option exists because the number of particle systems can easily grow very large.

  3. Thomas Larsson repo owner

    Using the resize option does not significantly affect the conversion time for me. But it probably depends on your machine. Mine is old but has lots of memory and processors.

    The main culprit is probably the conversion from edges to polylines. It is done in a python loop at line 496 in hair.py. Doing that loop in C would speed up conversion a lot, I think.

  4. Thomas Larsson repo owner

    Just to be clear, it is done in python because I don't know how to do it otherwise. Suggestions are appreciated.

    Example. The edges

    [0,1] [1,2] [3,4] [4,5] [5,6] [7,8] [9,10] [10,11]
    

    should become the polylines

    [0,1,2] [3,4,5,6] [7,8] [9,10,11]
    

  5. Alessandro Padovani reporter

    Thank you Thomas for the fast reply.

    The reason why multiple materials work for you is because you didn’t use the test scene. That is, you didn’t follow the steps to reproduce the issue. Specifically, the test scene doesn’t tessellate so about 800k edges are imported for the hair, but multiple materials are not preserved. If I tessellate then about 3800k edges are imported for the hair and multiple materials are preserved.

    I suspect that when we don’t tessellate the dforce guides are exported in the dbz, as opposed to the dforce strands. While for sbh both the guides and the strands are exported in different buffers as noted in #928 and we have it in the global settings. This information should be added to the docs.

    May be it is a requirement to tessellate the dforce hair to keep the multiple materials. If so it should be noted in the docs. Or may be the code can be fixed to preserve multiple materials with no tessellation, that is, for the dforce guides.

    As for the conversion speed I did two tests.

    The first test is with no resize, I splitted the hair in four parts and converted each part separately, this took about 12 minutes in total to convert all parts, using about 1G ram. This is about the same time and ram it takes to convert the hair without splitting. So it seems there's no gain splitting the hair.

    Then I also duplicated the hair cap and gave each hair part its own cap. This way the total time went from 15 minutes to 2 minutes. That is, it seems that adding multiple particle systems to an emitter takes a long time, while if we duplicate the scalp then add one single particle system to each emitter it is much faster. This could be a good optimization.

    Also, it is not important to duplicate the scalp, we could use a cube or a single point as emitter as far as we don't use the interpolated option for the children. As the interpolated option is not useful anyway to convert the daz hair.

    # no resize test
    12 minutes with unique hair
    12 minutes with splitted hair
     2 minutes with splitted hair and duplicated caps
    

    The test above is without the resize option. As for the resize option I can confirm that here using resize takes much longer. So I tried with one small part of the example above. I splitted the hair with the resize option and this way I got a better time. Here it's 60 seconds for the unique hair and 30 seconds for the hair splitted in two parts. So it seems that resize is sensible to splitting. Thus splitting hair and duplicating caps are both useful optimizations.

    # resize test
    60 seconds with unique hair
    30 seconds with splitted hair (two parts)
    

  6. Alessandro Padovani reporter

    note. If I understand it correctly. Please note that splitting the hair, that’s the principle of “separate by loose parts“, is a workaround because we’re not able to understand why the code is not linear with the number of strands. So instead of fixing the code we separate by loose parts. Of course it would be better to fix the code.

  7. Thomas Larsson repo owner

    OK, I understand. I first imported the file with Unmorphed fitting, and then everything worked out nicely. With DBZ fitting I see the problem as well. Since edges can’t have materials in Blender, the material numbers are read from the duf file, but that only contains materials for the unmorphed edge. So if the hair in the dbz file has more edges, material data is missing for some edges and the plugin assigns all edges to material no 1. This is what happens here: the duf file has 255797 vers and the dbz file has 811357.

    The solution is to save the polyline material numbers in the dbz file. I will work on that.

    First I also encountered a different problem: every sixth vertex on each strand have an extra connection, and that breaks havoc to the hair algorithm. This happens because the hair guides are somehow mixed with the tesselated hair. The solution is to turn off Hair Guides in this case.

  8. Alessandro Padovani reporter

    I always have the hair guides off in the global settings. But the plugin imports some hair even if I don’t tessellate in daz studio. That's why I am under the impression that without tesselation we could get the dforce guides.

    Please note that for dforce hair the PR tesselation works the same with the viewport line tessellation sides = 0 and 1.

    # imported dforce hair strands
    unmorphed: 250k edges (probably the dforce guides)
    dbz without tesselation: 250k edges (same as unmorphed)
    dbz with PS tessellation: 800k edges
    dbz with PS and PR tesselation: 3.800k edges
    

  9. Alessandro Padovani reporter

    note. Importing the dforce hair without PS PR tessellation (dforce guides) is very fast and correctly converts all the hair materials. Then we can use children to fill the hair strands. This is probably the best way to convert dforce hair to blender. Unfortunately this is not always good enough as we see in #928 where we need the tessellation to get a precise replica of the body hair.

  10. Thomas Larsson repo owner

    I think I may have got it now. I was making a number of assumptions that weren’t true in this case, e.g. that the first vertices were exclusively used for the hair guides and could be ignored. Now the plugin imports all vertices and all polylines, and things seems to work out right. You need to update the DS export scripts, because the polyline materials must be exported in order to pick them up in Blender.

    The performance problems seem to be gone too.

  11. Alessandro Padovani reporter

    Commit ca42f84 doesn’t work fine.

    It is much faster than before. Also dforce guides and PS hairs convert fine so the test scene curls.duf works fine.

    It doesn’t seem to work with PR hairs. I included a new test scene curls-2.duf which does PR tessellation. It worked before with the previous commit though it required 12 minutes to complete. Now without resize it ends in some seconds but the result is incomplete. With resize it takes 12 minutes with the same incomplete result.

    steps:

    1. import the test scene curls-2.duf as dbz
    2. make hair with multiple materials

  12. Midnight Arrow

    The new hair system is slated for Blender 3.3. It’s based on geometry nodes so it’s more flexible and robust than the hardcoded (about to be retired) particle system. Particle hair is end of life so they’re not bothering to fix its bugs.

    @Thomas There’s an alpha build if you want to test it. https://builder.blender.org/download/experimental/ Currently named “temp-hair-patches-33”.

  13. Alessandro Padovani reporter

    Despite the optimizations geometry nodes are still awful slow at least for now. Then if diffeo is supposed to stay compatible with 2.9-3.2 it makes sense to support particle hairs. Unless you mean 3.3 will drop the particle system entirely in favor of nodes so old scenes will not work anymore, that I hope it’s not the case.

  14. Midnight Arrow

    Other systems that got replaced (pose library, proxy armatures) were ditched pretty quick after they became obsolete. I assume particle hair will stick around for a few releases after geonodes hits feature parity with it. Then they’ll remove it and implement an autoconversion system (which may not be perfect) to convert them to geonodes.

    Geonodes may be somewhat slow in performance, but you can change materials by declaring an attribute per curve and giving it as a boolean to a Set Materials node, so there’s a gain in efficiency.

    I think I read one of the Blender devs say the particles code hasn’t been touched in years. If we decide to stick with particle hairs for speed and backwards compatibility, we need to acknowledge it’s very buggy and will stay that way since it’s end of life and isn't receiving any fixes or updates.

  15. Thomas Larsson repo owner

    All test files seem to work now. There were some unwarranted assumption left that should be eliminated now.

    The curls-2 file takes a long time to convert, but that is expected because it is a huge mesh. Here are some data:

    Curls-2, dbz:
    Verts: 3842707, Edges: 3570777, Faces: 0
    Total number of strands: 72874
    Hair converted in 411.74 seconds
    
    Curls-2, unmorphed:  
    Verts: 255797, Edges: 252960, Faces: 0
    Total number of strands: 2838
    Hair converted in 2.73 seconds
    
    Curls, dbz:  
    Verts: 811357, Edges: 581685, Faces: 0
    Total number of strands: 30616
    Hair converted in 32.28 seconds
    

    So the curls-2 file has 6 times as many edges as curls, and takes 8 times longer to convert. Sounds almost linear to me. Most of the time (400 seconds) is spent making the particle systems, whereas the initial analysis is completed in 10 seconds.

    Edit: yeah I know, 12 times. 400 seconds is not the same as 4 minutes.

  16. Thomas Larsson repo owner

    @Midnight: The Make Hair tool consists of two parts. The first makes the initial analysis, and extracts strand coordinates and materials from the mesh data. That data is then fed to a back-end which creates the particle systems. I expect that one should be able to replace the second part with a geometry nodes back-end, while keeping the first part unchanged. More or less. If geometry nodes allow variable strand lengths and multiple materials it is unnecessary to split the strands into separate groups.

  17. Midnight Arrow

    Taken in an earlier alpha. One has 10 points the other has 100. Same curve object.

    If I understand the design right, the Bezier curve object and hair curve guides will soon be the same thing. You create hair by passing a curve to act as a hair guide to a geometry node that interpolates child hairs. Since geometry nodes can already convert meshes to curves, the Make Hair tool may be wholly unnecessary. You maybe can pass in the mesh hair, have it nondestructively converted to curves, and use the curves to create the hair.

  18. Alessandro Padovani reporter

    @Thomas

    Your cpu is faster than mine here it takes 12 minutes to convert curls-2. Though I have 4 cores and only 25% of the cpu is used so I guess it's single threaded.

    As noted above, we can speed-up things quite a lot by using one single particle system per emitter. So we can either duplicate the scalp or use some cubes or single vertices as emitters. This way the time to convert curls-2 goes from 12 minutes to 1 minute in my test.

    As proof of concept I splitted the hair in four parts then converted each part with its own emitter, as shown below. I used some cubes as emitters. The total time for the conversion is about 1 minute vs 12 minutes for the unsplitted hair.

    steps:

    1. split the hair in four parts
    2. add four cubes that we’ll use as emitters, they have to be sized and located inside the scalp
    3. convert each part with its own emitter

    Please note that if we use cubes or single vertices as emitters, then it will probably not work for body hair since in that case the strands have to follow an animated mesh. So in that case we should duplicate the body mesh as emitters. But vertices are good for scalp hairs since the scalp is not animated. I did not test body hair so this is a guess.

    option name: "multiple emitters: none / mesh / vertices"
    option tooltip: "faster conversion, please use mesh for body hair and vertices for the scalp"
    

  19. Alessandro Padovani reporter

    note. important. That adding multiple particle systems to the same emitter is what takes time is a guess of mine based on the test above. It may very well be that the algorithm to create an emitter is quadratic with the number of particles. In this case splitting the hair in four parts as above would take about 1/16 of the time that may also be the case with the results I get.

    I have no way to test what is what but for sure we can speed up a lot.

  20. Midnight Arrow

    The algorithms used for particle hair baffle even the developers. In https://developer.blender.org/T91568, removing a subsurf modifier made the distribution time increase by ten times.

    Even back in 2016 Sergey Sharybin called it “really hairy and fragile and run below repairable state“. https://developer.blender.org/T46776

    It seems the people who coded it didn’t encapsulate very well, so it’s spaghetti code spread out everywhere in the application. And as I said, they’re not fixing it anymore (unless it was broken by 2.80 and above).

    Using particle hair now is like trying to keep a junker on the road with duct tape and prayer. Just something to keep in mind if you pursue this.

  21. Alessandro Padovani reporter

    From the articles pointed by Midnight it seems the particle system can hit O(n**2) if it is feeded with a reversed ordered set. Since in this case the qsort algorithm to sort the particles is O(n**2). So if the strands are ordered before feeding the emitter this could be the issue. The qsort complexity for a random set is O(n log n) that’s almost linear.

    Not sure this applies to our case it’s just what I got from the reading.

    p.s. And if the articles are right then this may very well happen in any part of blender using qsort, that has nothing to do with “spaghetti code“.

  22. Thomas Larsson repo owner

    I suspected that the problem was that the depsgraph is evaluated twice for each particle system. If that were true it should be faster to make the small particle systems first, but it didn’t matter. If the resulting hair is output as a polyline mesh the relevant material is stored now. The user could then convert each polyline mesh (one for each material) to particles, and perhaps use a fresh copies of the skull mesh as emitter. Perhaps it helps. Anyway, I don’t really want to put a lot of effort into the old hair system since it is about to be removed anyway.

  23. Alessandro Padovani reporter

    I agree the speed optimization can be done by the user. That is, splitting the hair into parts and duplicating the emitter. It must also be said that this is only needed for very large hairs in the order of some million edges. The docs already suggest to split the hair for large meshes but they don’t suggest to duplicate the emitter so perhaps this could be added.

    Or I could write a blog post but it’s very minimal as an argument. Let me know.

    edit. note. This is minor. As for commit b1991e2, if we “make polyline hair” then the hair is splitted by materials but the parts are no more parented to the armature. Also, since dforce/sbh hairs are already polylines, may be we could import them as splitted by default. Instead of having to convert polylines to polylines to get the split.

  24. Thomas Larsson repo owner

    The polyline parts now have the same parent and parent type as the original hair, and also belong to the same collection.

    Splitting polyline hair on import isn’t so easy. The hair is analyzed in the same way as particle hair, only the final step that generates the hair from the analyzed data is different. Also I don’t see the point. Unless the polyline hair is very big, it is more convenient to only make a single conversion.

  25. Alessandro Padovani reporter

    Do you want me to write a blog post about duplicating the emitter ? Or are you adding a note in the docs ?

  26. Thomas Larsson repo owner

    Please write a blog post. And you don’t have to ask me about it in the future, your contributions to the plugin have been invaluable even if it is me who write the code.

  27. Log in to comment