makehumancommunity / mpfb2

MPFB2 is a free and open source human generator for Blender
http://static.makehumancommunity.org/mpfb.html
Other
307 stars 36 forks source link

Enhancement: Metric/Imperial measurements mapping for circumferences and lengths in Model tab #168

Open maryam-fatima opened 8 months ago

maryam-fatima commented 8 months ago

Description of Desired Enhancement I would appreciate the inclusion of metric/imperial system mappings for circumference and length measurements within the model sub-panels currently available in 0.0 to 1.0 range. I am open to contribute to the repository and would appreciate guidance from the MPFB team in implementing this feature 🚀 .

Considered Alternatives I attempted using scripting for model generation in Make Human, which already has this feature, but encountered difficulties. On the other hand, working with the MPFB2 plugin in Blender has proven to be a more seamless experience, courtesy of the MPFB team and the well-organized scripting section within Blender 👏 .

P.S : I have some basic understanding of the mapping code in makehuman source code.

joepal1976 commented 8 months ago

I'm happy to assist in whatever way I can if you want to take a swipe at implementing the measure stuff.

My suggestion would be to put the core logic for converting to/from real world measurements in https://github.com/makehumancommunity/mpfb2/blob/master/src/mpfb/services/targetservice.py and then add a specific measure panel class under https://github.com/makehumancommunity/mpfb2/tree/master/src/mpfb/ui/model in the same way that the macro targets are placed in a separate file.

If you want, I can create a branch with a skeleton outline of the relevant code structures.

maryam-fatima commented 8 months ago

yes please, I'd greatly appreciate it if you could share the skeleton code.

joepal1976 commented 8 months ago

Ok, I've added a crude structure in https://github.com/makehumancommunity/mpfb2/commit/951504590403e2e2e6031337e36e123407e98ea0 (which is in the branch blender4_measure)

It currently draws measure subpanels with the appropriate targets listed, but doesn't really do any converting to/from real world values.

There is some core UI logic that is probably going to be tricky to get around here. The panel is defined at blender start up, ie before there is even a human in the scene. That is also where the properties' max and min values are defined (last lines in _measuresubpanel.py). But these values will most likely need to be dynamic. I mean, a fat muscular man will have a wider range for the waist circumference than a skinny baby, in world measurement terms.

maryam-fatima commented 8 months ago

tack så mycket!

maryam-fatima commented 8 months ago

Hi, I have started working on this and have done the debugging setup with vs code. I have a few questions for you. The approach I came up with is:

Apply the shape keys with max and min values in inputs and use these to calculate the sum of vertices distance or edge lengths (as done in Make Human in getMeasure) to find the maximum and minimum values in real world measurements on startup and when any phenotype value is changed. Then use these calculated values to go between scaled and real world values.

If this approach is fine, how can I get the vertices corresponding to the targets? what I know is that the MakeHuman using vertices list for measure targets in Ruler class to calculate the real world measurements. If it's not too much trouble, could you please provide more details on this as well? I am currently in the process of understanding the source code. and would really appreciate your guidance.

joepal1976 commented 8 months ago

Ok, let's see... There are a few different questions here. As I'm travelling and won't have access to a proper workstation for a few days, the following is a bit theoretical and untested, so bear with me if I say something dumb.

Calculating min/max values

Regarding the max/min values, you don't really need to apply a shape key to figure these out. It can be calculated from the current vertex positions and information about the target, which will most likely be more efficient in a UI situation.

Getting the target data

On line 554 and forward in https://github.com/makehumancommunity/mpfb2/blob/master/src/mpfb/services/targetservice.py you can see how a target is loaded from a file. With some intermediary steps it ends up in _target_string_to_shape_key_info() (line 302) where the text file is parsed into a dict which contains target coordinates.

As this is static data which is independent on the current shape of the human, it might make sense to load the measure targets on startup and cache the dicts. Possibly it might make sense to add a public method to TargetService which loads a target by path and returns a dict the first time, and subsequently returns a cached version of the dict.

Getting the current position of vertices on the human

You can get the vertex coordinates of the human after shape keys via bmesh:

depsgraph = bpy.context.evaluated_depsgraph_get()
bm = bmesh.new()
bm.from_object( basemesh, depsgraph )
bm.verts.ensure_lookup_table()
a_specific_coordinate = bm.verts[1000].co

This would return the current coordinate of vertex 1000.

Calculating the max/min position of a vertex

Now, targets are pretty simple creatures. If the target is applied at 0.0 and you want to know what the maximum position would be for a vertex with the target applied at 1.0, it is as easy as adding the target and the vertex coordinate together.

So assuming you have loaded a dict "target" with the target info and it has information about vertex 1000, you can get the vector to add to the vertex coordinate via the appropriate entry under verts

vector_to_add = Vector( [0.0, 0.0, 0.0] )
weight = 1.0
for vert in target["verts"]:
    if vert[0] == 1000: # If we're looking for vertex index 1000
        vector_to_add[0] = weight * vert[1] # X
        vector_to_add[1] = weight * vert[2] # Y
        vector_to_add[2] = weight * vert[3] # Z 

If the target is currently applied at 0.2, you'd have to add 80% of the target to get the vertex position at 1.0

Knowing which verts to calculate max/min for

There is currently no information about the ruler/circumference in MPFB. You'd have to copy/paste these data structures from the Ruler class in MakeHuman. The numbers in the array after the name of the measure target are the relevant vertex indices.

Strategy

Much of what I have written above might be sensible to implement as generic methods in TargetService, as it's generic logic not necessarily specific to measurements.

Regarding the UI logic for getting the measurements onto the screen, I suspect that having all of the above done on each cycle of draw() of the panel will be unviable and would make the blender UI unresponsive. A strategy for caching the values and only changing them when the target stack changes will need to be found. Note that not only the phenotype will have an influence: consider for example the pregnant belly target, which will have significant influence on the waist measure. So basically, a recalculation will need to be done if any target value has changed.

Long and wordy, but I hope at least some of it is helpful.

maryam-fatima commented 8 months ago

Thank you so much for the detailed answer despite your travel. Yes, this does make sense. I will give it a try and will update you on this soon. Another quick question, I was trying debugging using https://github.com/AlansCodeLog/blender-debugger-for-vscode and added a single checkpoint in _measuresubpanel.py. I found out that even on hover on the add-on the draw code runs. How can I change this behavior as I wanted to test the case where the value for certain box is updated but it keeps going back to code immediately when I bring the cursor on the add-on display.

image image

Is there any specific debugging mechanism/way that you use or you would like to suggest.

joepal1976 commented 8 months ago

Can't be of much help there, I'm afraid. I don't work via a debugger at all. Instead I tend to add debug output via _LOG and run unit tests.

maryam-fatima commented 8 months ago

oh okay, where can I view these logs? I opened the logs folder in mpfb and the combined.txt does not say much.

image image
joepal1976 commented 8 months ago

You can set the debug level for a specific log channel on the developer panel. The default is INFO, which doesn't output very much.

Another approach is to temporarily set the log level in the file you're working with. Where the _LOG constant is defined, which is in the top of most files:

_LOG.set_level(LogService.DEBUG)

You can also see the debug output in the console window, which is available in the "window" main menu when running Blender on windows. I don't know if it's there on mac.

maryam-fatima commented 8 months ago

alright, thank you! The console window is not there in Mac but running blender through terminal does the trick.

joepal1976 commented 8 months ago

I've added #171 with a suggestion for a caching mechanism for the UI.

maryam-fatima commented 8 months ago

Found the fix for this issue ( last bullet in issues to be addressed ) from #170 and resolved it.

"Inputs for the measure panel targets cannot be set through terminal as apparently the name is not a valid modifier. I tried converting it to a valid modifier but got another error that property of the [new name] is not defined."

Previously I added the measurement mapping related code to _modelsubpanel.py as well for measure related inputs as I was testing through terminal and that wasn't working for _measuresubpanel. Should I remove those now or keep them there?

joepal1976 commented 8 months ago

It'd probably make sense to keep the UI-related measure code as much as possible inside the _measuresubpanel file. If some parts have to be in the other panel files, then that's ok. But if it's possible to choose, then keep it in _measuresubpanel.